Ответ 1
Да, это можно сделать в ограниченной степени.
Но сначала нам понадобится
{-# LANGUAGE Rank2Types #-}
Определим
data M a b = M { name :: Int -> String -> String, eval :: a -> b }
Я добавляю больше структуры к вашим именам, чтобы я мог получить более приятную поддержку шоу.;)
Затем определим класс:
class Magic m where
magic :: M a b -> m a b
instance Magic M where
magic = id
instance Magic (->) where
magic (M _ f) = f
Теперь рассмотрим тип:
type MyFunc a b = forall m. Magic m => m a b
Результатом типа magic
является либо (a -> b)
, либо M a b
.
Поэтому он может использоваться как член MyFunc
. Теперь этот тип несколько неудовлетворен, потому что вы не можете делать экземпляры отправки на него, но это означает, что
inc :: MyFunc Int Int
inc = magic (M (const (showString "inc")) (+1))
test :: Int
test = inc 1
отлично работает.
Мы можем даже сделать довольно хороший способ показать их. Хотя мы не можем использовать show на MyFunc
, мы можем определить его для M
.
instance Show (M a b) where
showsPrec d (M s _) = s d
Тогда мы можем сделать функцию, которую мы можем применить к M a b
(и по расширению любой MyFunc
), чтобы вывести a M a b
.
m :: M a b -> M a b
m = id
и мы можем определить специальный комбинатор, чтобы показать MyFunc
s:
showM :: MyFunc a b -> String
showM f = show (m f)
Тогда мы можем играть. Мы можем определить композиции 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
И поскольку я дал достаточно структуры именам, мы даже получим правильную скобку для более сложных композиций без лишних круглых скобок.
*Main> showM $ inc2 .# inc
"(inc . inc) . inc"
*Main> showM $ inc .# inc2
"inc . inc . inc"
Но помните, вы не сможете определить какие-либо экземпляры для MyFunc
, так как это может быть только type
, а не newtype
. Чтобы определить экземпляры, вам нужно будет определить их на M
, а затем используйте M
для преобразования в этот тип, чтобы неявная отправка имела тип, который нужно захватить.
Из-за типа ранга 2, если вы сильно используете их в локальных контекстах, вы также можете включить NoMonoLocalBinds
и/или NoMonomorphismRestriction
.