Uwaga, można łańcuch dowolną liczbę <*>
, aby uzyskać funkcję postaci
f (a0 -> .. -> an) -> (f a0 -> .. -> f an)
Jeśli mamy typ a0 -> .. -> an
i f a0 -> .. -> f an
, możemy obliczyć z tego f
. Możemy zakodować tę relację, a najbardziej ogólny typ, co następuje
class Lift a f b | a b -> f where
lift' :: f a -> b
Jak można się spodziewać, „rekurencyjne przypadek” wystąpienie po prostu zastosować <*>
raz, potem 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
Podstawa przypadek jest wtedy, gdy nie ma więcej funkcji. Ponieważ nie można rzeczywiście stwierdzić „a
nie jest typem funkcji”, to opiera się na nakładających przypadkach:
instance (f a ~ b) => Lift a f b where
lift' = id
powodu zasad selekcji instancja GHCs, rekurencyjne sprawa zawsze będzie wybrany, jeśli to możliwe.
Następnie funkcja chcesz to lift' . pure
:
lift :: (Lift a f b, Applicative f) => a -> b
lift x = lift' (pure x)
To gdzie zależność funkcjonalna na Lift
staje się bardzo ważne. Ponieważ f
jest wymienione tylko w kontekście, funkcja ta byłaby źle wpisana, chyba że możemy ustalić, co f
zna tylko a
i b
(które pojawiają się po prawej stronie =>
).
wymaga kilka rozszerzeń:
{-# LANGUAGE
OverlappingInstances
, MultiParamTypeClasses
, UndecidableInstances
, FunctionalDependencies
, ScopedTypeVariables
, TypeFamilies
, FlexibleInstances
#-}
i, jak zwykle o zmiennej liczbie argumentów funkcji w Haskell, zazwyczaj jedynym sposobem, aby wybrać instancję jest dać wyraźny podpis typu.
lift (\x y z -> x * y + z) readLn readLn readLn :: IO Int
Sposób Pisałem go, GHC chętnie akceptują lift
który jest polimorficzny w argumentach do f
(ale sama w sobie nie f
).
lift (+) [1..5] [3..5] :: (Enum a, Num a) => [a]
Czasami kontekst jest wystarczający do określenia właściwego typu. Zwróć uwagę, że typ argumentu jest ponownie polimorficzny.
main = lift (\x y z -> x * y + z) readLn readLn readLn >>= print
Od GHC> = 7,10, OverlappingInstances
została zaniechana i kompilator wyśle ostrzeżenie. Prawdopodobnie zostanie usunięty w późniejszej wersji. Można to naprawić poprzez usunięcie OverlappingInstances
od {-# LANGUAGE .. #-}
Pragma i zmieniając 2. wystąpienie do
instance {-# OVERLAPS #-} (f a ~ b) => Lift a f b where
Jakiego rodzaju próbujesz go podnieść? Widzę '<*>' w kodzie, ale nie widzę żadnej wzmianki o 'Applicative' w sygnaturach typu ... Tak czy inaczej, podejrzewam, że wystąpienie' Podnieś a' będzie problemem, ponieważ nakłada się z każda inna możliwa instancja (w tym 'Lift (a -> r)'). – MathematicalOrchid
Nieważne, mój kod, próbowałem mnóstwo rzeczy (prawdopodobnie bzdury), właśnie wysłał kilka losowych migawki dla dobra tego. Jestem bardzo zdezorientowany tą koncepcją, ponieważ na przykład 'lift (pure (+)) (Just 1) (Just 2)' - here, '(pure (+))' ma inny typ niż 'Just 1 ', ale dostarczona struktura jest zakodowana dla pojedynczego typu' Integer' ... Potrzebuję również sposobu kodowania instancji dla "dowolnego typu, który nie jest funkcją", jako pewnego rodzaju warunku zakończenia dla rozwijania typu. – MaiaVictor