1

我不明白为什么 Haskell 无法在以下代码中找出第 8 行的类型。expressMaybe 函数的类型签名不是确定结果类型与两个输入参数的类型相同吗?

{-# LANGUAGE MultiParamTypeClasses #-}

class Gene g n where
  express :: g -> g -> g
  -- there will be other functions that use the "n" type parameter

expressMaybe :: Gene g n => Maybe g -> Maybe g -> Maybe g
expressMaybe (Just a) (Just b) = Just (express a b) -- line 8
expressMaybe (Just a) Nothing  = Just a
expressMaybe Nothing (Just b)  = Just b
expressMaybe Nothing Nothing   = Nothing

我得到的错误是:

Amy20.hs:8:40:
    Ambiguous type variable `n0' in the constraint:
      (Gene g n0) arising from a use of `express'
    Probable fix: add a type signature that fixes these type variable(s)
    In the first argument of `Just', namely `(express a b)'
    In the expression: Just (express a b)
    In an equation for `expressMaybe':
        expressMaybe (Just a) (Just b) = Just (express a b)
Failed, modules loaded: none.

我尝试使用 RankNTypes 和 ScopedTypeVariables,但我不知道如何使错误消失。

预先感谢您的帮助!

编辑:既然我理解了这个问题,我使用了fundeps,因为我对它们很熟悉,而且对于我的应用程序来说,拥有多个用于编码基因的“字母表”没有多大意义。不过,我以前从未使用过类型族,所以我也会研究一下。

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

class Gene g n | g -> n where
  express :: g -> g -> g
  -- there will be other functions that use the "n" type parameter

expressMaybe :: Gene g n => Maybe g -> Maybe g -> Maybe g
expressMaybe (Just a) (Just b) = Just (express a b) -- line 8
expressMaybe (Just a) Nothing  = Just a
expressMaybe Nothing (Just b)  = Just b
expressMaybe Nothing Nothing   = Nothing
4

2 回答 2

12

考虑一下:

instance Gene String Int where
  express _ _ = "INT"

instance Gene String Double where
  express _ _ = "DOUBLE"

expressMaybe (Just "") (Just "")

该代码应该产生(Just "INT")还是(Just "DOUBLE")?是的,Haskell 知道 的结果expressMaybe将与参数具有相同的类型。但这并不意味着它知道在这里使用哪个实例,因为同一类型可以有多个实例,gn.

如果在您的情况下n,每种类型只有一种类型g,您可以考虑使用类型族或功能依赖项等扩展,您可以使用这些扩展向类型系统表达这一事实。

于 2012-10-09T10:32:26.293 回答
1

我建议您避免功能依赖并选择类型系列。它们更有趣、更直观:

{-# LANGUAGE TypeFamilies, KindSignatures #-}

class Gene g where
  type Nucleotide g :: *  -- each instance has an associated type
  express :: g -> g -> g
  encode :: [Nucleotide g] -> g -- probably doesn't make sense, but need an example
  -- there will be other functions that use the "Nucleotide g" type parameter

假设我们有

data ACTG = A | C | T | G
data ACTGgene = ACTGgene [ACTG]

本质上,您通过类型级别的简单模式匹配来定义类型函数:

instance Gene ACTGgene where
  type Nucleotide ACTGgene = ACTG
  encode ns = ACTGgene ns
  express = error "I'm out of my depth here because I gave up Biology when I hit 14."

现在我们的代码编译:

expressMaybe :: Gene g => Maybe g -> Maybe g -> Maybe g
expressMaybe (Just a) (Just b) = Just (express a b) -- compiles fine
expressMaybe (Just a) Nothing  = Just a
expressMaybe Nothing (Just b)  = Just b
expressMaybe Nothing Nothing   = Nothing

现在可能是您的基因类型基本上取决于您使用的核苷酸,或者您可以互换使用不同的核苷酸,我不知道,但是解决您的问题的一种可能的解决方案是使用构造函数类:

class Gene g where
   express :: g n -> g n -> g n
   encode :: n -> g n
   -- more stuff

这将适合数据声明,如

data ACTG = A | C | T | G
data ListGene n = ListGene [n]

instance Gene ListGene where
  ...

但也许这对于您的问题域来说太参数化了,我不知道。


当然,您应该尝试类型族。我做到了。现在我爱他们。- 链接的答案包括最后的链接以供进一步阅读。

于 2012-10-10T00:26:10.530 回答