在Haskell中,我从未完全接受的一件事是,如何拥有多态常量和函数,其返回类型不能由它们的输入类型决定,例如
class Foo a where
foo::Int -> a我不喜欢这样做的一些原因:
“在Haskell中,给定相同的输入,函数总是返回相同的输出”,但这是真的吗?read "3"在Int上下文中使用时返回3,而在(比方说) (Int,Int)上下文中使用时会抛出错误。是的,你可以说read也接受一个类型参数,但是在我看来,类型参数的含蓄性使它失去了一些美。
哈斯克尔最烦人的事之一。如果我错了,请纠正我,但是MR的全部原因是看起来是共享的计算可能不是因为类型参数是隐式的。
哈斯克尔最烦人的事情之一。例如,如果将函数输出中的多态结果传递给输入中的多态函数,则会发生。同样,如果我错了,请纠正我,但是如果函数的返回类型不能由它们的输入类型(以及多态常量)决定,则这是不必要的。
因此,我的问题是(冒着被盖章为“讨论队列”的风险):在类型检查器不允许此类定义的情况下,是否有可能创建类似Haskell的语言?如果是的话,这种限制的好处/缺点是什么?
我可以看到一些眼前的问题:
比方说,如果2只有Integer类型,那么2/3将不再使用/的当前定义键入check。但在这种情况下,我认为具有函数依赖的类型类可以挽救(是的,我知道这是一个扩展)。此外,我认为拥有可以接受不同输入类型的函数比在其输入类型中受限的函数更直观,但我们只是将多态值传递给它们。
在我看来,像[]和Nothing这样的值的输入似乎是一个更难破解的难题。我还没想出一个好办法来对付他们。
发布于 2011-09-03 18:50:27
实际上,我认为返回类型多态性是类型类的最佳特性之一。在使用了一段时间之后,有时我很难回到没有OOP风格的建模中去。
考虑代数的编码。在Haskell中,我们有一个类型类Monoid (忽略mconcat)
class Monoid a where
mempty :: a
mappend :: a -> a -> a我们如何在OO语言中将其编码为接口呢?简单地说,我们不能,因为mempty的类型是(Monoid a) => a,也就是返回类型的多态。具有模拟代数的能力是非常有用的,IMO。
你以抱怨“参考透明度”开始你的帖子。这就提出了一个重要的问题: Haskell是一种面向价值的语言。因此,像read 3这样的表达式不必被理解为计算值的东西,它们也可以被理解为值。这意味着真正的问题不是返回类型多态性:它是具有多态类型的值([]和Nothing)。如果语言应该具有这些特性,那么它确实必须具有多态返回类型才能保持一致性。
我们应该可以说[]是forall a. [a]类型的吗?我也这么想。这些特性非常有用,它们使语言更加简单。
如果Haskell有亚型多态性,那么[]可以是所有[a]的子类型。问题是,我不知道在没有空列表类型的情况下编码是多态的。考虑一下在Scala中是如何实现的(它比使用规范静态类型化的OOP语言( Java)更短)
abstract class List[A]
case class Nil[A] extends List[A]
case class Cons[A](h: A. t: List[A]) extends List[A]即使在这里,Nil()也是Nil[A] **类型的对象。
返回类型多态性的另一个优点是它使Curry嵌入更加简单。
考虑以下逻辑定理:
t1 = forall P. forall Q. P -> P or Q
t2 = forall P. forall Q. P -> Q or P我们可以在Haskell中以定理的形式来捕捉这些定理:
data Either a b = Left a | Right b
t1 :: a -> Either a b
t1 = Left
t2 :: a -> Either b a
t2 = Right总之:我喜欢返回类型多态性,并且只有当您对值有一个有限的概念时,才认为它破坏了引用的透明度(尽管在特殊类型类的情况下,这一点不那么令人信服)。另一方面,我确实觉得你关于先生和类型违约的观点很有说服力。
*。在注释艾斯克斯指出中,这不是严格意义上的:我们可以通过将代数建模为另一种类型来重新实现类型类。就像java:
abstract class Monoid<M>{
abstract M empty();
abstract M append(M m1, M m2);
}然后,您必须将这种类型的对象传递给您。Scala有一个隐式参数的概念,它避免了一些(但在我的经验中并不是全部)显式管理这些东西的开销。将您的实用方法(工厂方法、二进制方法等)放在一个单独的F有界类型上,这是一种非常好的方法,可以用支持泛型的面向对象语言来管理事物。尽管如此,如果我没有用打字机来建模的经验的话,我不确定我会不会摸索这个模式,而且我也不确定其他人会这样做。
它也有局限性,开箱即用无法获得实现任意类型类型的类型的对象。您必须显式传递值,使用类似Scala的内嵌,或者使用某种形式的依赖注入技术。生活变得丑陋。另一方面,您可以为同一类型拥有多个实现,这是很好的。某种东西可以是一个多方面的单调词。另外,单独进行这些结构会使国际海事组织在数学上更现代化,更有建设性。所以,虽然我仍然喜欢Haskell的方式来做这件事,但我可能夸大了我的情况。
带有返回类型多态性的Typeclasses使得这类事情更容易处理。这并不意味着这是最好的方法。
**。J rg W Mittag指出 --在Scala中,这并不是一种规范的方法。相反,我们将使用更类似于以下内容的标准库:
abstract class List[+A] ...
case class Cons[A](head: A, tail: List[A]) extends List[A] ...
case object Nil extends List[Nothing] ...这利用了Scala对底层类型以及协变量类型参数的支持。因此,Nil是Nil类型,而不是Nil[A]类型。在这一点上,我们离Haskell相当远,但是注意Haskell是如何表示底部类型的,这是很有趣的。
undefined :: forall a. a也就是说,它不是所有类型的子类型,它是所有类型的成员(Sp)。
还有更多的返回类型多态性。
发布于 2011-09-24 16:37:15
我只想指出一个误解:
“在Haskell中,给定相同的输入,函数总是返回相同的输出”,但这是真的吗?在Int上下文中使用时,读取"3“返回3,但在(例如,Int,Int)上下文中使用时会抛出一个错误。
我妻子开斯巴鲁和我开斯巴鲁并不意味着我们开的是同一辆车。仅仅因为有两个名为read的函数,并不意味着它是同一个函数。实际上,read :: String -> Int是在Int的Read实例中定义的,其中read :: String (Int, Int)是在(Int,Int)的读实例中定义的。因此,它们是完全不同的功能。
这种现象在编程语言中很常见,通常称为重载。
发布于 2016-01-18 01:00:22
我真希望“返回类型多态性”这个术语从未被创建过。它助长了对正在发生的事情的误解。可以这么说,虽然消除“返回类型多态性”将是一种极具即时性和表现力的改变,但它甚至无法解决问题中提出的问题。
返回类型一点也不特殊。考虑:
class Foo a where
foo :: Maybe a -> Bool
x = foo Nothing此代码导致与“返回类型多态性”相同的所有问题,并演示了与OOP相同类型的差异。然而,没有人谈论“第一个论点可能是类型多态”。
关键思想是,实现使用类型来解析要使用的实例。任何类型的(运行时)值都与其无关。实际上,即使对于没有值的类型,它也是有效的。特别是,Haskell程序没有类型就没有意义。(具有讽刺意味的是,这使得哈斯克尔成为教会式的语言,而不是咖喱式的语言。我在作品中有一篇关于这方面的博客文章。)
https://softwareengineering.stackexchange.com/questions/105662
复制相似问题