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

har*_*arr 8 polymorphism haskell interface typeclass

我知道有两种方法可以将接口规范与Haskell中该接口的实现分开:

  1. 类型类,例如:

  2. 记录,例如:

问题1:什么时候适合使用其中一种?

问题2:在Haskell中分离接口/ impl有哪些其他方法?

Eri*_*lun 4

\n\n

问题 1 的答案非常简单:这两个选项等效于 \xe2\x80\x94 类型类,可以“脱糖”为仅数据类型。这个想法已在http://www.haskellforall.com/2012/05/scrap-your-type-classes.html中进行了描述和论证。中进行了描述和论证。

\n\n
\n\n

问题 2 的答案是,这两种是将接口与实现分开的唯一方法。推理是这样的:

\n\n
    \n
  1. 最终目标是以某种方式传递函数 \xe2\x80\x94 这是因为 Haskell 中除了函数之外没有其他方法可以实现任何东西,所以为了传递实现,你需要传递函数(注意规格只是类型)
  2. \n
  3. 您可以传递单个函数或多个函数
  4. \n
  5. 要传递单个函数,您只需传递该函数,或者将该函数包装在模仿类型类的东西中(即,除了CanFoo类型签名()之外,还为您的接口提供一个名称(例如a -> Foo
  6. \n
  7. 要传递多个函数,您只需在元组或记录内传递它们(就像我们的CanFoo,但有更多字段);请注意,在此上下文中,记录只是具有命名字段的命名元组类型。
  8. \n
\n\n

\xe2\x80\x94 正如已经证明的那样,无论是显式还是隐式(使用类型类)传递函数,在概念上都是一回事[1]

\n\n
\n\n

下面简单演示了这两种方法的等效性:

\n\n
data Foo = Foo\n\n-- using type classes\nclass CanFoo a where\n  foo :: a -> Foo\n\ndoFoo :: CanFoo a => a -> IO Foo\ndoFoo a = do\n  putStrLn "hello"\n  return $ foo a\n\ninstance CanFoo Int where\n  foo _ = Foo\n\nmain = doFoo 3\n\n-- using explicit instance passing\ndata CanFoo\' a = CanFoo\' { foo :: a -> Foo }\n\ndoFoo\' :: CanFoo\' a -> a -> IO Foo\ndoFoo\' cf a = do\n  putStrLn "hello"\n  return $ (foo cf) a\n\nintCanFoo = CanFoo { foo = \\_ -> Foo }\n\nmain\' = doFoo\' intCanFoo 3\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如您所看到的,如果您使用记录,您的“实例”将不再自动查找,而是您需要将它们显式传递给需要它们的函数。

\n\n

另请注意,在简单的情况下,记录方法可以简化为仅传递函数,因为传递CanFoo { foo = \\_ -> Foo }实际上与传递包装函数本身相同\\_ -> Foo

\n\n
\n\n

[1]

\n\n

事实上,在 Scala 中,这种概念上的等价性变得显而易见,因为 Scala 中的类型类是根据类型(例如trait CanFoo[T])、该类型的许多值以及标记为 的该类型的函数参数进行编码的implicit,这将使 ScalaCanFoo[Int]在调用站点查找类型的值。

\n\n
// data Foo = Foo\ncase object Foo\n\n// data CanFoo t = CanFoo { foo :: t -> Foo }\ntrait CanFoo[T] { def foo(x : T): Foo }\nobject CanFoo {\n  // intCanFoo = CanFoo { foo = \\_ -> Foo }\n  implicit val intCanFoo = new CanFoo[Int] { def foo(_: Int) = Foo }\n}\n\nobject MyApp {\n  // doFoo :: CanFoo Int -> Int -> IO ()\n  def doFoo(someInt: Int)(implicit ev : CanFoo[Int]] = {\n    println("hello")\n    ev.foo(someInt)\n  }\n\n  def main(args : List[String]) = {\n    doFoo(3)\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n