O per essere precisi, perché usiamo foldr per codificare le liste e l'iterazione per codificare i numeri?Perché utilizziamo le pieghe per codificare i tipi di dati come funzioni?
Scusate per l'introduzione a lungo termine, ma non so davvero come nominare le cose che voglio chiedere, quindi prima dovrò dare un po 'di esposizione. Questo richiama pesantemente lo this C.A.McCann post che non soddisfa proprio la mia curiosità e mi occuperò anche dei problemi con rank-n-types e infinite cose pigre.
Un modo per codificare tipi di dati come funzioni è quello di creare una funzione di "pattern matching" che riceve un argomento per ogni caso, ogni argomento essendo una funzione che riceve i valori corrispondenti a quel costruttore e tutti gli argomenti ritorno uno stesso tipo di risultato.
Questo tutto funziona come previsto per i tipi non-ricorsivi
--encoding data Bool = true | False
type Bool r = r -> r -> r
true :: Bool r
true = \ct cf -> ct
false :: Bool r
false = \ct cf -> cf
--encoding data Either a b = Left a | Right b
type Either a b r = (a -> r) -> (b -> r) -> r
left :: a -> Either a b r
left x = \cl cr -> cl x
right :: b -> Either a b r
right y = \cl cr -> cr y
Tuttavia, la bella analogia con il modello di corrispondenza rompe con tipi ricorsivi. Potremmo essere tentati di fare qualcosa di simile
--encoding data Nat = Z | S Nat
type RecNat r = r -> (RecNat -> r) -> r
zero = \cz cs -> cz
succ n = \cz cs -> cs n
-- encoding data List a = Nil | Cons a (List a)
type RecListType a r = r -> (a -> RecListType -> r) -> r
nil = \cnil ccons -> cnil
cons x xs = \cnil ccons -> ccons x xs
ma non siamo in grado di scrivere quelle definizioni ricorsive di tipo in Haskell! La solita soluzione consiste nel forzare il callback del caso cons/succ a essere applicato a tutti i livelli di ricorsione invece del solo primo (cioè, scrivere un fold/iteratore). In questa versione si usa il tipo di ritorno r
cui il tipo ricorsiva sarebbe:
--encoding data Nat = Z | S Nat
type Nat r = r -> (r -> r) -> r
zero = \cz cf -> cz
succ n = \cz cf -> cf (n cz cf)
-- encoding data List a = Nil | Cons a (List a)
type recListType a r = r -> (a -> r -> r) -> r
nil = \z f -> z
cons x xs = \z f -> f x (xs z f)
Mentre questa versione funziona, rende definire alcune funzioni molto più difficile. Ad esempio, scrivere una funzione "coda" per gli elenchi o una funzione "predecessore" per i numeri è banale se è possibile utilizzare la corrispondenza dei modelli, ma diventa complicato se invece è necessario utilizzare le pieghe.
Così sulle mie vere domande:
Come possiamo essere sicuri che la codifica utilizzando pieghe è potente come il "pattern matching codifica" ipotetica? C'è un modo per prendere una definizione di funzione arbitraria tramite la corrispondenza del modello e convertirla meccanicamente in una sola utilizzando invece le piegature? (In tal caso, ciò contribuirebbe anche a rendere meno magiche definizioni come tail o foldl in termini di foldr e meno magico)
Perché il sistema di tipi Haskell non consente i tipi ricorsivi necessari nella "corrispondenza di modello" la codifica?. C'è una ragione per consentire solo tipi ricorsivi in tipi di dati definiti tramite
data
? Il pattern matching è l'unico modo per consumare direttamente i tipi di dati algebrici ricorsivi? Ha a che fare con l'algoritmo di inferenza del tipo?
Mi limiterò a lasciare questo qui: http://www.haskell.org/pipermail/haskell-cafe/2010-November/085897.html – melpomene
@melpomene: Hmm, sembra così come le domande # 2 - tu può farlo in Haskel ma è necessario utilizzare newtypes per ottenere l'accesso alla ricorsività. Ora ho solo bisogno di vedere se quei documenti che hai linkato rispondono anche a qustion # 1 :) (btw, your [link] (http://www.haskell.org/pipermail/haskell-cafe/2010-November/085897.html) è stata copiata copiata strana) – hugomg
Sebbene il predecessore per il Nat sia difficile, l'aggiunta è 'O (1)' (penso che sia ancora più efficiente di solito dei computer in versione binaria). – PyRulez