是否可以使用您自己的数据类型模拟具有GHC扩展名的函数?我想做的是例如。
(虚构语法)
data MyFunc = MyFunc String (Int->Int)
instance (Int->Int) MyFunc where
($) (MyFunc _ f) i = f i
inc = MyFunc "increment" (1+)
test = inc 1
即。带有一些元信息的数据,可以进行模式匹配,但仍可以像常规函数一样调用。现在,我知道我可以定义自己的中缀运算符,如$$
并调用inc $$ 1
,但是能够使用常规函数调用语法在嵌入式DSL中非常有用。
答案 0 :(得分:18)
是的,可以在有限的范围内完成。
但首先我们需要
{-# 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
工作正常。
我们甚至可以用一种相当不错的方式来展示它们。即使我们无法在MyFunc
上使用show,我们也可以为M
定义。
instance Show (M a b) where
showsPrec d (M s _) = s d
然后我们可以制作一个我们可以应用于M a b
(以及任何MyFunc
扩展名)的函数来获取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
。
答案 1 :(得分:5)
不,语法f e
无法重载。 f
必须包含S -> T
类型。
但是如果你进行深度嵌入,你仍然可以用EDSL做很多事情,即你让你的函数构建语法树而不是计算。
答案 2 :(得分:3)
你不能直接重载函数调用语法,没有。
你可以制作自己的自定义箭头类型---请参阅Control.Arrow。然后,您可以使用arrow notation调用“函数”(您需要在源文件的顶部包含{-# LANGUAGE Arrows #-}
)。这对您来说是否足够好取决于您的DSL需求。