Haskell, comparando um genérico a um Char

Nov 06 2020

Eu preciso fazer uma função Haskell que tenha uma entrada genérica, mas produza um booleano dependendo se o genérico corresponde a certos dígitos / caracteres algo ao longo das linhas de:

checkFunct :: a -> Bool
checkFunct x = (elem x ['a','b','c'])

Como isso não vai funcionar porque você não pode comparar Char com genérico. Eu procuro há anos e sou novo em Haskell, então não tenho idéia de como consertar. Nota: É declarado especificamente que a entrada deve ser um -> Bool

Por exemplo, este código funciona perfeitamente:

checkFunct :: Char -> String
p x = elem x ["a","b","c"]

No entanto, eu preciso de 'um' em vez de Char. Desculpe por qualquer confusão. (Aparentemente, 'a' não é genérico)

Respostas

3 leftaroundabout Nov 06 2020 at 02:43

Uma possível interpretação da questão é: você deseja que a função informe se o argumento é a Chare está na lista . Você não pode fazer isso apenas com a assinatura a -> Bool, porque as informações de tipo não estão disponíveis para funções, no entanto, você pode exigir informações de tipo explicitamente: há uma classe dedicada para isso .

import Data.Typeable

checkFunct :: Typeable a => a -> Bool
checkFunct x = case cast x of
    Just xc  -> xc `elem` ['a','b','c']
    Nothing  -> False

(Observe que, ao contrário de muitas outras linguagens, castnão converte realmente os tipos , apenas corresponde se era o tipo de destino em primeiro lugar.)

*Main> checkFunct 'b'
True
*Main> checkFunct 'f'
False
*Main> checkFunct 37
False
*Main> checkFunct "a"
False
3 JonPurdy Nov 06 2020 at 04:05

Por si só, uma função do tipo a -> Bool, que é abreviação de forall a. a -> Bool, não é muito útil, uma vez que deve funcionar “para todos a” que o chamador especifica, portanto, não é permitido saber nada sobre a. É por isso que você está recebendo respostas dizendo basicamente “você não pode chegar lá a partir daqui”.

(Na verdade, existem apenas duas funções totais puras com este tipo: alwaysTrue _ = Truee alwaysFalse _ = False.)

Mas estou supondo, com base neste comentário seu:

Eu preciso de uma função que tem uma entrada :: a -> Boole precisa calcular se aé um item em uma lista especificada.

Que sua pergunta está no contexto de uma função como esta:

contains :: (a -> Bool) -> [a] -> Bool

Agora podemos fazer algo útil! Veja, este tipo é abreviação de forall a. (a -> Bool) -> [a] -> Bool, ou seja, também opera em valores de qualquer tipo a, mas novamente, o chamador desta função é aquele que escolhe ae passa o predicado do tipo a -> Bool para algum específicoa , bem como uma lista de avalores. Então containsnão sabe nada sobre a, mas não sabe que ele pode aplicar o predicado para os elementos da lista, uma vez que está garantido para ser do mesmo tipo.

Portanto, por exemplo, você pode chamar esta função com um argumento do tipo Char -> Bool, que é definido acomo Char, ou um do tipo Int -> Bool, que é definido acomo Inte assim por diante:

isABC :: Char -> Bool
isABC x = x `elem` "abc"

-- contains :: (a -> Bool) -> [a] -> Bool
-- contains isABC :: [Char] -> Bool
-- contains isABC "yeah" == True
-- contains isABC "nope" == False

is123 :: Int -> Bool
is123 x = x >= 1 && x <= 3

-- contains is123 :: [Int] -> Bool
-- contains is123 [] == False
-- contains is123 [0] == False
-- contains is123 [0, 1] == True

Se você deseja escrever essa containsfunção sozinho, pode usar uma definição recursiva, com esta estrutura geral:

contains :: (a -> Bool) -> [a] -> Bool
contains predicate list = case list of

  -- How can you test the current element with the predicate?
  -- What should you do when the predicate returns true?
  -- How can you test the rest of the list?
  current : rest -> _

  -- What should you return if you’ve reached the end?
  [] -> _

Os sublinhados são chamados de buracos digitados , que você pode incluir em seu programa para pedir ao GHC que diga que tipo está esperando lá e dê algumas sugestões de funções e variáveis ​​no escopo que podem caber. À medida que você descobre como preencher mais detalhes do seu programa, pode usar mais furos para solicitar os tipos de peças mais específicas, por exemplo, você pode substituir o primeiro furo por if _ then _ else _.

Algo que pode ser útil aqui é a TypeApplicationsextensão (habilitada no GHCi com :set -XTypeApplicationsou em um arquivo de origem com {-# LANGUAGE TypeApplications #-}), que permite que você escreva os argumentos de tipo explicitamente, por exemplo, no GHCi:

> :set -XTypeApplications

> contains :: (a -> Bool) -> [a] -> Bool; contains = error "TODO: implement 'contains'"

> :type contains @Char
contains @Char :: (Char -> Bool) -> [Char] -> Bool

> :type contains @Int
contains @Int :: (Int -> Bool) -> [Int] -> Bool

> :t map
map :: (a -> b) -> [a] -> [b]

> :t map @Double
map @Double :: (Double -> b) -> [Double] -> [b]

> :t map @Double @Int
map @Double @Int :: (Double -> Int) -> [Double] -> [Int]
1 JohnF.Miller Nov 06 2020 at 02:37

E o que você gostaria de fazer se afor algo parecido b -> FreeT (PipeF a b)?

Seu tipo diz que posso entregar a você qualquer função e você descobrirá Boolque existem apenas duas maneiras de escrever essa função. checkFunct _ = TrueecheckFunct _ = False

Você precisa dizer ao compilador um pouco mais sobre o que espera como entrada.

A partir do seu exemplo de implementação, suponho que você pode querer ver se a representação da string de x está em uma lista.

Você poderia escrever:

checkFunct :: Show a => a -> Bool
checkFunct x = (elem (show x) ["a","b","c"])

O Show a =>diz que tudo o que eu der como entrada para esta função, show adeve retornar uma string.

Ou talvez você queira escrever sua própria aula

class ToChar a where
  toChar :: a -> Char

Você poderia então escrever:

checkFunct :: ToChar a=> a -> Bool
checkFunct x = (elem (toChar x) ['a','b','c'])

Para usar 'checkFunct', você precisará fornecer uma implementação de toChar para qualquer tipo que deseja usar com essa função.

import Data.Char
instance ToChar Int where
  toChar = chr