si noti che è possibile concatenare qualsiasi numero di <*>
, per ottenere una funzione della forma
f (a0 -> .. -> an) -> (f a0 -> .. -> f an)
Se abbiamo il tipo a0 -> .. -> an
e f a0 -> .. -> f an
, possiamo calcolare f
da questo. Siamo in grado di codificare questo rapporto, e il tipo più generale, come segue
class Lift a f b | a b -> f where
lift' :: f a -> b
Come ci si potrebbe aspettare, l'istanza "caso ricorsivo" sarà semplicemente applicare <*>
una volta, poi recurse:
instance (a ~ a', f' ~ f, Lift as f rs, Applicative f)
=> Lift (a -> as) f (f' a' -> rs) where
lift' f a = lift' $ f <*> a
La base il caso è quando non c'è più funzione. Dal momento che non si può effettivamente affermi "a
non è un tipo di funzione", questo si basa su casi di sovrapposizione:
instance (f a ~ b) => Lift a f b where
lift' = id
A causa di regole di selezione esempio GHCs, sarà sempre possibile selezionare il caso ricorsivo, se possibile.
Allora la funzione che si desidera è lift' . pure
:
lift :: (Lift a f b, Applicative f) => a -> b
lift x = lift' (pure x)
Questo è dove la dipendenza funzionale su Lift
diventa molto importante. Poiché f
è menzionato solo nel contesto, questa funzione sarebbe mal tipata a meno che non siamo in grado di determinare che cosa è f
che conosce solo a
e (che appaiono nella parte destra di =>
).
ciò richiede diverse estensioni:
{-# LANGUAGE
OverlappingInstances
, MultiParamTypeClasses
, UndecidableInstances
, FunctionalDependencies
, ScopedTypeVariables
, TypeFamilies
, FlexibleInstances
#-}
e, come al solito con funzioni variadic Haskell, normalmente l'unico modo per selezionare un'istanza è di dare una firma di tipo esplicito.
lift (\x y z -> x * y + z) readLn readLn readLn :: IO Int
Il modo in cui ho scritto, GHC sarà lieto di accettare lift
che è polimorfa negli argomenti per f
(ma non f
stesso).
lift (+) [1..5] [3..5] :: (Enum a, Num a) => [a]
A volte il contesto è sufficiente per inferire il tipo corretto. Si noti che il tipo di argomento è di nuovo polimorfico.
main = lift (\x y z -> x * y + z) readLn readLn readLn >>= print
Come di GHC> = 7,10, OverlappingInstances
è stato deprecato e il compilatore emetterà un avvertimento. Probabilmente verrà rimosso in una versione successiva. Questo può essere risolto rimuovendo OverlappingInstances
dal {-# LANGUAGE .. #-}
pragma e cambiando il 2 ° istanza
instance {-# OVERLAPS #-} (f a ~ b) => Lift a f b where
Che tipo stai cercando di sollevarlo _to_? Vedo '<*>' nel codice, ma non vedo alcuna menzione di 'Applicativo' nelle firme dei tipi ... Ad ogni modo, sospetto che l'istanza di' Lift a' sarà un problema, poiché si sovrappone a ogni altra possibile istanza (incluso 'Lift (a -> r)'). – MathematicalOrchid
Non importa il mio codice, ho provato un sacco di cose (molto probabilmente senza senso), appena pubblicato qualche snapshot casuale per il gusto di farlo. Sono davvero confuso con questo concetto, perché, ad esempio, 'lift (pure (+)) (Just 1) (Just 2)' - here, '(pure (+))' ha un tipo diverso da 'Just 1 ', ma la struttura fornita è hardcoded per un singolo tipo" Integer "... Ho anche bisogno di un modo per codificare un'istanza per" qualsiasi tipo che non è una funzione ", come una sorta di condizione di terminazione per il tipo di srotolamento. – MaiaVictor