Tak, można to zrobić w ograniczonym zakresie.
Ale najpierw musimy
{-# LANGUAGE Rank2Types #-}
Zdefiniujmy
data M a b = M { name :: Int -> String -> String, eval :: a -> b }
Dodaję więcej strukturę swoimi nazwami, więc mogę uzyskać ładniejszy wsparcie show. ;)
Następnie pozwala określić klasę:
class Magic m where
magic :: M a b -> m a b
instance Magic M where
magic = id
instance Magic (->) where
magic (M _ f) = f
Teraz pod uwagę rodzaj:
type MyFunc a b = forall m. Magic m => m a b
Typ wynikiem magic
jest albo (a -> b)
lub M a b
.
Dzięki temu może być używany jako członek MyFunc
. Teraz, ten typ jest nieco niezadowalająca, ponieważ nie można zrobić instancje wysyłką na nim, ale to nie znaczy, że
inc :: MyFunc Int Int
inc = magic (M (const (showString "inc")) (+1))
test :: Int
test = inc 1
działa dobrze.
Możemy nawet zrobić raczej fajny sposób na pokazanie ich. Mimo że nie możemy użyć programu na MyFunc
, możemy go zdefiniować dla M
.
instance Show (M a b) where
showsPrec d (M s _) = s d
Wtedy możemy utworzyć funkcję możemy zastosować do M a b
(i co za tym idzie każdy MyFunc
), aby wydostać się M a b
.
m :: M a b -> M a b
m = id
i możemy zdefiniować specjalny COMBINATOR pokazać MyFunc
s:
showM :: MyFunc a b -> String
showM f = show (m f)
Wtedy możemy grać. Możemy zdefiniować kompozycje MyFunc
s.
infixr 9 .#
(.#) :: MyFunc b c -> MyFunc a b -> MyFunc a c
f .# g = magic (M
(\d -> showParen (d > 9) $ showsPrec 10 (m f) .
showString " . " .
showsPrec 9 (m g))
(f . g))
inc2 :: MyFunc Int Int
inc2 = inc .# inc
test2 :: Int
test2 = inc2 1
bar, baz :: String
bar = showM inc
baz = showM inc2
I dlatego dałem tyle strukturę nazw, możemy nawet uzyskać prawidłową parenthesization dla bardziej skomplikowanych kompozycjach, bez zbędnych nawiasów.
*Main> showM $ inc2 .# inc
"(inc . inc) . inc"
*Main> showM $ inc .# inc2
"inc . inc . inc"
Należy jednak pamiętać, że nie będzie w stanie określić żadnych wystąpień dla MyFunc
, ponieważ może to być tylko type
, a nie newtype
. Aby zdefiniować instancje, musisz zdefiniować je na M
, a następnie użyć m
, aby przekonwertować na ten typ, tak aby niejawne wywoływanie miało typ, który można przechwycić.
Ze względu na typ 2 kategorii, jeśli używasz ich mocno w lokalnych kontekstach, możesz również włączyć NoMonoLocalBinds
i/lub NoMonomorphismRestriction
.
To jest trochę przerażające. Kocham to. –