Sí, se puede hacer de forma limitada.
Pero primero tendremos que
{-# LANGUAGE Rank2Types #-}
Definamos
data M a b = M { name :: Int -> String -> String, eval :: a -> b }
estoy añadiendo más estructura a sus nombres para que pueda obtener el apoyo más agradable espectáculo. ;)
luego le permite definir una clase:
class Magic m where
magic :: M a b -> m a b
instance Magic M where
magic = id
instance Magic (->) where
magic (M _ f) = f
Ahora, considere el tipo:
type MyFunc a b = forall m. Magic m => m a b
El tipo de resultado de magic
es bien (a -> b)
o una M a b
.
Puede usarse como miembro de MyFunc
. Ahora bien, este tipo es algo insatisfactorio, porque no se puede crear instancias despachan en él, pero sí quiere decir que
inc :: MyFunc Int Int
inc = magic (M (const (showString "inc")) (+1))
test :: Int
test = inc 1
funciona bien.
Incluso podemos hacer una forma bastante agradable de mostrarlos. Aunque no podemos usar show en MyFunc
, podemos definirlo para M
.
instance Show (M a b) where
showsPrec d (M s _) = s d
Entonces podemos hacer una función podemos aplicar a M a b
(y por extensión cualquier MyFunc
) para salir de una M a b
.
m :: M a b -> M a b
m = id
y podemos definir un combinador especial para mostrar MyFunc
s:
showM :: MyFunc a b -> String
showM f = show (m f)
entonces podemos jugar. Podemos definir composiciones de 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
Y porque di suficiente estructura para los nombres, que incluso llegar parentización correcta para composiciones más complicadas, sin paréntesis innecesarios.
*Main> showM $ inc2 .# inc
"(inc . inc) . inc"
*Main> showM $ inc .# inc2
"inc . inc . inc"
Pero recuerde, usted no será capaz de definir todas las instancias de MyFunc
, ya que sólo puede ser un type
, y no un newtype
. Para definir las instancias, tendrá que definirlas en M
, y luego usar m
para convertirlas a ese tipo, de modo que el despacho implícito tenga un tipo al que agarrarse.
Debido al tipo de rango 2, si utiliza estos en gran medida en contextos locales, es posible que también desee activar NoMonoLocalBinds
y/o NoMonomorphismRestriction
.
Esto es un poco aterrador. Lo amo. –