是否可以使用自己的数据类型来模拟一个函数? [英] Is it possible to emulate a function using your own data type?

查看:112
本文介绍了是否可以使用自己的数据类型来模拟一个函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以使用您自己的数据类型来模拟具有一些GHC扩展的函数?我想要做的是例如

(虚构语法)

 数据MyFunc = MyFunc String(Int-> Int)

实例(Int-> Int)MyFunc其中
($)(MyFunc_f)i = fi

inc = MyFuncincrement(1+)

test = inc 1

Ie数据与它携带一些元信息,可以进行模式匹配,但仍然可以像常规函数那样调用。现在,我知道我可以定义自己的中缀操作符如 $$ ,并调用 inc $$ 1 ,但是能够使用常规函数调用语法在嵌入式DSL中非常有用。

解决方案

是的,它可以做到有限范围。



但首先我们需要

  { - # LANGUAGE Rank2Types# - } 

让我们定义

  data M ab = M {name :: Int  - >字符串 - >字符串,eval :: a  - > b} 

我将更多结构添加到您的名字中,以便获得更好的展示支持。 ;)

然后定义一个类:

  class Magic m其中
magic :: M ab - > mab

实例Magic M其中
magic = id

实例Magic( - >)其中
magic(M_f)= f

现在,请考虑类型:

 键入MyFunc ab = forall m。 Magic m => mab 

结果类型 magic (a - > b)或a M ab



因此它可以用作 MyFunc 的成员。现在,这种类型有点令人不满意,因为你不能派生实例,但它确实意味着:

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

test :: Int
test = inc 1

工作得很好。



我们甚至可以做一个相当不错的方式来展示他们。即使我们无法在 MyFunc 上使用show,我们可以将它定义为 M

  instance显示(M ab)其中
显示Prec d(M s _)= sd

然后我们可以创建一个我们可以应用于 M ab 的函数(以及任何

  m :: M ab  - > M 
m = id

我们可以定义一个特殊的组合符来显示 MyFunc s:

  showM :: MyFunc ab  - >字符串
showM f = show(m f)

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

  infixr 9。#
(。#):: MyFunc bc - > MyFunc a b - > MyFunc ac
f。#g = magic(M
(\d-> showParen(d> 9)$ showsPrec 10(mf)。
showString。。
表示Prec 9(mg))
(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 定义任何实例,因为它只能是类型,而不是 NEWTYPE 。为了定义实例,您必须在 M 中定义它们,然后使用 m 转换为该类型所以隐式调度有一个类型可以抓取。

由于rank 2类型,如果在本地环境中使用这些类型,您可能还需要打开 NoMonoLocalBinds 和/或 NoMonomorphismRestriction


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.

解决方案

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.

这篇关于是否可以使用自己的数据类型来模拟一个函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆