是否可以使用您自己的数据类型来模拟函数?

发布于 2024-11-16 23:42:14 字数 381 浏览 3 评论 0原文

是否可以使用您自己的数据类型和某些 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 中。

Is it possible to emulate a function with your own data type with some GHC extension? What I want to do is e.g.

(imaginary syntax)

data MyFunc = MyFunc String (Int->Int)

instance (Int->Int) MyFunc where
    ($) (MyFunc _ f) i = f i

inc = MyFunc "increment" (1+)

test = inc 1

I.e. data that carries some meta-information with it and can be pattern matched, but which can still be called like a regular function. Now, I know that I could define my own infix operator like $$ and call inc $$ 1, but being able to use the regular function call syntax would be very useful in embedded DSLs.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

爱的那么颓废 2024-11-23 23:42:14

是的,可以在有限的范围内做到这一点。

但首先我们需要

{-# 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) 或 a 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

我们可以定义一个特殊的组合器来显示MyFuncs:

showM :: MyFunc a b -> String
showM f = show (m f)

然后我们就可以玩了。我们可以定义 MyFunc 的组合。

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

Yes, it can be done to a limited extent.

But first we'll need

{-# LANGUAGE Rank2Types #-}

Let's define

data M a b = M { name :: Int -> String -> String, eval :: a -> b }

I'm adding more structure to your names so I can get nicer show support. ;)

Then lets define a class:

class Magic m where
    magic :: M a b -> m a b

instance Magic M where
    magic = id

instance Magic (->) where
    magic (M _ f) = f

Now, consider the type:

type MyFunc a b = forall m. Magic m => m a b

The result type of magic is either (a -> b) or a M a b.

So it can be used as a member of MyFunc. Now, this type is somewhat unsatisfying, because you can't make instances dispatch on it, but it does mean that

inc :: MyFunc Int Int
inc = magic (M (const (showString "inc")) (+1))

test :: Int
test = inc 1

works just fine.

We can even make a rather nice way to show them. Even though we can't use show on MyFunc, we can define it for M.

instance Show (M a b) where
    showsPrec d (M s _) = s d

Then we can make a function we can apply to M a b (and by extension any MyFunc) to get out an M a b.

m :: M a b -> M a b
m = id

and we can define a special combinator to show MyFuncs:

showM :: MyFunc a b -> String
showM f = show (m f)

Then we can play. We can define compositions of MyFuncs.

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

And because I gave enough structure to the names, we even get correct parenthesization for more complicated compositions, without needless parentheses.

*Main> showM $ inc2 .# inc
"(inc . inc) . inc"

*Main> showM $ inc .# inc2
"inc . inc . inc"

But remember, you won't be able to define any instances for MyFunc, since it can only be a type, and not a newtype. In order to define instances you'll have to define them on M, and then use m to convert to that type so that implicit dispatch has a type to grab onto.

Because of the rank 2 type, if you use these heavily in local contexts, you may also want to turn on NoMonoLocalBinds and/or NoMonomorphismRestriction.

我喜欢麦丽素 2024-11-23 23:42:14

不可以,语法 f e 不能重载。 f 必须具有类型 S -> T。

但是,如果您进行深度嵌入,您仍然可以使用 EDSL 做很多事情,即让您的函数构建语法树而不是计算。

No, the syntax f e cannot be overloaded. The f has to have type S -> T.

But you can still do a lot with EDSLs if you do a deep embedding, i.e., you let your functions build syntax trees instead of computing.

梦旅人picnic 2024-11-23 23:42:14

您不能直接重载函数调用语法,不。

可以制作自己的自定义箭头类型 --- 请参阅Control.Arrow.然后,您可以使用箭头符号 (您需要在源文件顶部包含 {-# LANGUAGE Arrows #-})。这对您来说是否足够好取决于您的 DSL 的需求。

You can't directly overload function call syntax, no.

You can make your own custom arrow type --- see Control.Arrow. You can then invoke your "functions" using arrow notation (you will need to include {-# LANGUAGE Arrows #-} at the top of your source file). Whether this is good enough for you depends on the needs of your DSL.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文