Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell:如何将接口与实现分离_Haskell_Interface_Polymorphism_Typeclass - Fatal编程技术网

Haskell:如何将接口与实现分离

Haskell:如何将接口与实现分离,haskell,interface,polymorphism,typeclass,Haskell,Interface,Polymorphism,Typeclass,我知道在Haskell中将接口规范与接口实现分离的两种方法: 类型类别,例如: 接口: 执行:StdGen 记录,例如: 接口: impl: 问题1:什么时候使用一个或另一个合适 问题2:在Haskell中,还有什么其他方法可以分离接口/impl?问题1的答案很简单:这两个选项是等效的-类型类可以“删除”为数据类型。这一想法已被描述,并被论证支持 问题2的答案是,这两种方法是分离接口和实现的唯一方法。理由如下: 最终目标是以某种方式传递函数——这是因为在Haskell中,除了作为函数之

我知道在Haskell中将接口规范与接口实现分离的两种方法:

  • 类型类别,例如:

    • 接口:
    • 执行:
      StdGen
  • 记录,例如:

    • 接口:
    • impl:
  • 问题1:什么时候使用一个或另一个合适


    问题2:在Haskell中,还有什么其他方法可以分离接口/impl?

    问题1的答案很简单:这两个选项是等效的-类型类可以“删除”为数据类型。这一想法已被描述,并被论证支持


    问题2的答案是,这两种方法是分离接口和实现的唯一方法。理由如下:

  • 最终目标是以某种方式传递函数——这是因为在Haskell中,除了作为函数之外,没有其他方法可以实现任何东西,所以为了传递实现,您需要传递函数(注意,规范只是类型)
  • 可以传递单个函数,也可以传递多个函数
  • 要传递单个函数,您只需传递该函数,或将该函数包装在模仿类型类的东西中(即,除了类型签名(
    a->Foo
    )之外,还要给接口一个名称(例如,
    CanFoo
  • 要传递多个函数,只需在元组或记录中传递它们(就像我们的
    CanFoo
    ,但有更多字段);请注意,在本上下文中,记录只是一个有命名字段的命名元组类型
  • -如前所述,显式还是隐式(使用类型类)传递函数在概念上是一回事[1]


    以下是两种方法如何等效的简单演示:

    data Foo=Foo
    --使用类型类
    CanFoo a班在哪里
    foo::a->foo
    doFoo::CanFoo a=>a->IO Foo
    doFoo a=do
    putStrLn“你好”
    返回$foo a
    实例CanFoo Int在哪里
    foo=foo
    main=doFoo 3
    --使用显式实例传递
    数据CanFoo'a=CanFoo'{foo::a->foo}
    doFoo'::CanFoo'a->a->IO Foo
    doFoo'cf a=do
    putStrLn“你好”
    返回$(foo cf)a
    intCanFoo=CanFoo{foo=\\\\\->foo}
    main'=doFoo'intCanFoo 3
    
    如您所见,如果使用记录,您的“实例”将不再自动查找,而是需要显式地将它们传递给需要它们的函数

    还要注意的是,在一个简单的例子中,record方法可以简化为只传递函数,因为传递
    CanFoo{foo=\\\\\\\\->foo}
    实际上与传递包装函数
    \\\\\\->foo
    本身是一样的


    [1]

    事实上,在Scala中,当Scala中的类型类按照类型(例如
    trait CanFoo[T]
    )、该类型的许多值以及标记为
    implicit
    的该类型的函数参数进行编码时,这种概念上的等价性就变得很明显,这将导致Scala查找类型
    CanFoo[Int]的值
    在呼叫站点

    //数据Foo=Foo
    案例对象Foo
    //数据CanFoo t=CanFoo{foo::t->foo}
    trait CanFoo[T]{def foo(x:T):foo}
    对象CanFoo{
    //intCanFoo=CanFoo{foo=\\\\\->foo}
    隐式val intCanFoo=new CanFoo[Int]{def foo(u:Int)=foo}
    }
    对象MyApp{
    //doFoo::CanFoo Int->Int->IO()
    def doFoo(someInt:Int)(隐式ev:CanFoo[Int]={
    println(“你好”)
    ev.foo(someInt)
    }
    def main(参数:列表[字符串])={
    多福(3)
    }
    }
    
    当您希望每种类型都有一个唯一的实现,或者您想按其特定类型来标记实现,或者您只是需要隐式实例解析的便利性时,请使用类型类。否则,请使用记录。但这只是我的观点……我认为至少第1部分是基于观点的(有些人几乎在任何情况下都是一方胜于另一方的顽固支持者……)。您可能会发现这一点很有用:类型类不是用于与规范分离的接口。它们用于引入上下文相关的重载符号。类型类作为模块的一个问题是,在类型类可见的范围内,所有实例都可见。@user2407038您的建议遵循Gabriel在nex提供的链接中所说的内容不要评论。谢谢。@Sibi-谢谢你的链接。Gabriel总是有很好的建议。我个人觉得typeclass示例比另一个更容易理解。我不明白为什么很多人反对他们。至于记录方法(如
    doFoo'
    ),你可以启用
    记录通配符
    并编写
    doFoo'CanFoo'{..}a=do{…;return(fooa)}
    。这使得函数在语法上更加出色,即使记录字段是中缀二进制函数(即
    data Num a=Num{(+)::a->a->a}