Para mim, parece que você sempre pode passar argumentos de função em vez de usar uma classe de tipo. Por exemplo, em vez de definir a classe de classe de igualdade:
class Eq a where
(==) :: a -> a -> Bool
E usá-lo em outras funções para indicar o argumento de tipo deve ser uma instância de Eq
:
elem :: (Eq a) => a -> [a] -> Bool
Não podemos simplesmente definir nossa elem
função sem usar uma classe de tipo e passar um argumento de função que faz o trabalho?
Monad m
restrição me diz mais do que passar argumentos de função adicionais dos tiposa -> m a
em a -> (a -> m b) -> m b
.TypeApplications
extensão permite explicitar o argumento implícito.(==) @Int 3 5
compara3
e5
especificamente comoInt
valores. Você pode pensar@Int
como uma chave no dicionário de funções de igualdade específicas de tipo, em vez daInt
função de comparação específica em si.Respostas:
Sim. Isso é chamado de "estilo de passagem de dicionário". Às vezes, quando estou fazendo algumas coisas especialmente complicadas, preciso descartar uma classe e transformá-la em dicionário, porque a passagem de dicionário é mais poderosa 1 , mas muitas vezes bastante complicada, tornando o código conceitualmente simples bastante complicado. Às vezes, uso o estilo de passagem de dicionário em idiomas que não são Haskell para simular classes tipográficas (mas aprendi que geralmente não é uma idéia tão boa quanto parece).
Obviamente, sempre que houver uma diferença no poder expressivo, haverá uma troca. Embora você possa usar uma determinada API de mais maneiras, se for escrita usando DPS, a API obtém mais informações, se você não puder. Uma das maneiras pelas
Data.Set
quais isso aparece na prática é o fato de existir apenas umOrd
dicionário por tipo. EleSet
armazena seus elementos classificados de acordo comOrd
, e se você criar um conjunto com um dicionário e, em seguida, inserir um elemento usando outro, como seria possível com o DPS, poderá quebrarSet
a invariante e causar a falha. Esse problema de exclusividade pode ser atenuado usando um método existencial fantasmadigite para marcar o dicionário, mas, novamente, à custa de um pouco de complexidade irritante na API. Isso também aparece da mesma maneira naTypeable
API.A parte da singularidade não aparece com muita frequência. O que as classes tipográficas são ótimas é escrever código para você. Por exemplo,
que usa dois "processadores" que recebem uma entrada e podem dar uma saída e os concatenam, achatando
Nothing
, teriam que ser escritos no DPS, algo assim:Basicamente, tivemos que especificar o tipo em que o estamos usando novamente, mesmo que já o tenhamos explicado na assinatura do tipo, e isso foi redundante porque o compilador já conhece todos os tipos. Como existe apenas uma maneira de construir um dado
Semigroup
em um tipo, o compilador pode fazer isso por você. Isso tem um efeito de tipo "juros compostos" quando você começa a definir várias instâncias paramétricas e a usar a estrutura de seus tipos para calcular para você, como nosData.Functor.*
combinadores, e isso é usado com grande efeito,deriving via
onde você pode obter basicamente todas as estrutura algébrica "padrão" do seu tipo, escrita para você.E nem me inicie nos MPTC e nos fundeps, que alimentam as informações de volta em digitação e inferência. Eu nunca tentei converter uma coisa dessas para DPS - suspeito que envolva repassar muitas provas de igualdade de tipo - mas, de qualquer forma, tenho certeza de que seria muito mais trabalho para o meu cérebro do que seria confortável com.
-
1 U NLES você usa
reflection
caso em que eles se tornam equivalentes em poder -, masreflection
também pode ser complicado de usar.fonte
Sim. Isso (chamado passagem de dicionário) é basicamente o que o compilador faz para as aulas de qualquer maneira. Para essa função, feita literalmente, ficaria assim:
Chamar
elemBy (==) x xs
agora é equivalente aelem x xs
. E, neste caso específico, você pode ir um passo além: sempreeq
tem o mesmo primeiro argumento, para que seja responsabilidade do chamador aplicá-lo e acabar com isso:Chamar
elemBy2 (x ==) xs
agora é equivalente aelem x xs
....Oh espere. Isso é só
any
. (E, de fato, na biblioteca padrãoelem = any . (==)
,.)fonte
implicit
e o compilador os injeta no escopo.