为什么不能将类的实例用作值?

Zhe*_*lov 2 haskell

为什么我不能这样写:

data Color = R | G | B deriving Show

showColor :: Show Color
showColor = Show Color

main = do
    putStrLn (showColor.show R)
    putStrLn (showColor.show G)
Run Code Online (Sandbox Code Playgroud)

为什么类的实例不是Haskell中的一等公民?

chi*_*chi 5

很难讨论为什么语言的某个特征是这样的,因为只有语言的设计者才能真正回答这个问题.然而,他们确定每个类型每个类最多只能有一个实例.这很可能是因为实例是隐含使用的.考虑一堂课

class Foo a where foo :: a -> String
Run Code Online (Sandbox Code Playgroud)

和这个模块

import A  -- defines an instance Foo Int
bar :: Int -> String
bar n = "The number is " ++ foo n
Run Code Online (Sandbox Code Playgroud)

bar函数隐式引用模块A的实例.现在假设我们添加另一个导入

import A  -- defines an instance Foo Int
import B  -- defines another instance Foo Int
bar :: Int -> String
bar n = "The number is " ++ foo n
Run Code Online (Sandbox Code Playgroud)

现在这是模棱两可的.Haskell可能提供了一种语法来消除歧义,但却选择了禁止它.一个优点是,无论谁读取代码都可以更容易地找到所使用的实例,因为只能有一个.

但是,可以部分模拟多个实例.GHC扩展允许定义隐式参数,允许在每个函数调用时为这些参数指定不同的"实例":

{-# LANGUAGE ImplicitParams #-}

data Color = R | G | B

showColor :: (?showC :: Color -> String)  => Color -> String
showColor c = "The color is: " ++ ?showC c

main :: IO ()
main = do
   let ?showC = \c -> case c of R -> "Red" ; _ -> "Not Red"
     in putStrLn (showColor B)
   let ?showC = \c -> case c of G -> "Green" ; _ -> "Not Green"
     in putStrLn (showColor B)
Run Code Online (Sandbox Code Playgroud)

以上的输出是:

The color is: Not Red
The color is: Not Green
Run Code Online (Sandbox Code Playgroud)

Agda编程语言没有类型类,但具有扮演类似角色的隐式实例.在那里,隐式传递一个实例(如在Haskell中),但是如果需要,您可以手动覆盖隐式参数并使用特殊语法来指定不同的实例.

  • 仅允许一个实例的最大原因是"连贯性".如果我构建一个`Map`或`HashMap`或者其他什么,并且我为不同的操作选择了不同的`Ord`或`Hashable`实例,那么在我开始得到错误的答案和/或奇怪的错误之前不久. (2认同)