【问题标题】:When to use typeclasses?什么时候使用类型类?
【发布时间】:2013-11-20 04:31:06
【问题描述】:

通常当我创建函数时,我不知道在哪些函数中使用类型类(EqIntegral 等)更好,因为有时不需要像这样使用它们:

factorial :: Int -> Int 
bla bla bla 
bla bla bla

factorial :: (Integral a) => a -> a
bla bla bla
bla bla bla

我相信第二个只是需要时间和地点 但是在elem函数中写Eq很重要(elleme下面是elem

elemme :: Eq a => a -> [a] -> Bool
y `elemme` [] = False
y `elemme` (x:xs) = if y == x then True else y `elemme` xs 

请给我一些建议。谢谢。

【问题讨论】:

  • 这可能会对您有所帮助:stackoverflow.com/questions/17100036/…
  • 想想函数/参数属性。你想要你的函数/参数有什么属性?首先,了解属性(EqOrdMonoid,...)然后,您可以限制您的函数使用参数(合规性)属性(而不是像这样的显式参数Int, Bool, ...)。
  • factorial 实际上是一个不好的例子,因为如果你使用Int 而不是Integer 它会给出任何大于 20 的错误答案。
  • @JeremyList 可以说,是的。但是仍然可能有一些用例用于类似的事情......如果我无法想到它们,请原谅我,但我确信它们就在那里。

标签: haskell


【解决方案1】:

类型类让您可以编写更通用的函数。 Haskell 有很棒的工具可以让你指定一个函数适用于所有类型,比如

id :: a -> a
id a = a

但有些事情并不适用于所有类型,

(==) :: Eq a => a -> a -> Bool

如果没有类型类,我们要么必须为每种类型编写一个新的==

eqInteger :: Integer -> Integer -> Bool

或者指定所有类型都是相等的并且可以测试相等性。但是你可以问

id == const 1

因此,当您发现自己想要指定您的函数适用于所有类型的子集时,请选择类型类。

我经常喜欢编写这种更通用的函数,即使我不会在一种以上类型上使用它们,因为签名越通用,实现的选项就越少,这让我更容易知道我没有没做过什么傻事

id :: Integer -> Integer
id = (+1)

您实际上是在声明您的函数只需要 X 个函数,这使得使用错误函数可能会出现编译器错误。

【讨论】:

    【解决方案2】:

    使我的函数尽可能通用的好处之一是它通常允许我以前没有想到的用例。当你开始把事情通用化时,你就会开始为你的函数想出一些奇怪的用途。

    你会说,“但是如果这个值是一个函数呢?”或者“如果我允许任何函子这样做会怎样——其他函子的结果会是什么?它有用吗?”事实证明,在很多情况下它!在我看来,这也是 Haskell 如此出色的原因之一。很容易“不小心”创建可重用的函数。

    在其他语言中,您为特定目的设计函数并以这种方式使用它们。在 Haskell 中,您为特定目的设计函数并给它们一个通用类型签名,突然之间,它们适用于大量您没有设计它们的情况。


    @jozefg 对限制可能实现的一般性提出了很好的观点。我只是想更加关注它,因为它实际上是一个非常强大的概念。通过一些通用函数,您实际上可以完全确定该函数仅基于类型签名做什么,因为该通用签名只有一种可能的实现。

    我最近遇到了签名

    mystery :: (a -> b -> c) -> (a -> b) -> a -> c
    

    这很有趣,因为我们实际上可以根据它的作用来确定它是哪个功能。我们有

    mystery :: (a -> b -> c) -> (a -> b) -> a -> c
    mystery    f                g           x = ...
    

    我们需要它返回某种c 值。我们获得c 类型值的唯一方法是将函数f 应用于a 值和b 值。我们已经有了一个 a 值——这是 x 参数。所以我们可以部分应用f x :: b -> c,但我们仍然需要一个b 值来获得我们想要的c 值。

    当然,解决方案是应用g x 来获得b 值,然后我们可以将其插入f,从而最终返回c 值。对此的描述有点复杂,但如果你在脑海中解决它,你就会到达

    mystery f g x = f x (g x)
    

    它与库函数ap 执行相同的操作。 (来自Control.Applicative。)

    非常很酷,您可以仅根据函数的类型签名找出函数的作用!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-05
      • 1970-01-01
      • 2020-03-05
      • 2015-04-05
      • 2010-11-17
      • 2011-09-03
      • 1970-01-01
      相关资源
      最近更新 更多