我有以下方法:
nucleotideComplement :: Char -> Either String Char
nucleotideComplement 'G' = Right 'C'
nucleotideComplement 'C' = Right 'G'
nucleotideComplement 'T' = Right 'A'
nucleotideComplement 'A' = Right 'U'
nucleotideComplement x = Left "Not a valid RNA nucleotide."
想要定义另一个:
toRNA :: String -> String
toRNA = either error mapM nucleotideComplement
但是我在这里遇到了类型错误。但是这样做似乎解决了这个问题:
toRNA :: String -> String
toRNA = either error id . mapM nucleotideComplement
我不明白为什么会发生这种情况
首先,id
的类型为a -> a
。接下来,在获取:t
和mapM nucleotideComplement
的类型(id . mapM nucleotideComplement
)时,它们似乎是相同的。为什么我会有这样不同的效果?
希望有人能够进一步澄清这一点。
答案 0 :(得分:12)
我认为你正在读这个错误......
either error id . mapM nucleotideComplement
你似乎认为这意味着
either error (id . mapM nucleotideComplement)
实际上它意味着
(either error id) . (mapM nucleotideComplement)
你在任何地方都没有id . mapM nucleotideComplement
。您正在应用mapM
,然后将结果传递给either
,这将适用error
或id
,具体取决于它是Left
还是{{} 1}}。
答案 1 :(得分:3)
两者的类型是(a -> c) -> (b -> c) -> Either a b -> c
。因此,您将其应用于error
,然后获得(b -> c) -> Either String b -> c
,然后将其应用于mapM
,然后获得Monad m => Either String (a -> m b) -> [a] -> m [b]
。然后,您将其应用于nucleotideComplement
并收到错误,因为nucleotideComplement
是一个函数,而不是Either
。
换句话说,如果您打算使用两个参数调用它,则将either
应用于三个参数,其中第二个参数是将mapM
应用于nucleotideComplement
的结果。要使用您想要的参数调用函数,您可以编写either error (mapM nucleotideComponent)
,但仍然无法工作,因为either
的第二个参数应该是接受Char
的函数(因为你有一个Either String Char
),而不是一个接受monad的人。要实现您想要的目标,您可以撰写either error nucleotideComponent
或使用.
,因为您已经找到了。
.
的版本有效,因为Haskell的优先规则说either error id . mapM nucleotideComplement
等同于(either error id) . (mapM nucleotideComplement)
,而不是(either error id . mapM) nucleotideComplement
或either error (id . mapM nucleotideComplement)
。 either error id
是一个将Either String b
转换为Either a b
的函数,其中左侧案例会导致错误,mapM nucleotideComplement
是一个将m Char
转换为另一个m Char
的函数m
,其中char为"翻转"对于任何monad m - 在这种情况下Either String
为Either String Char
。因此,通过编写这两个函数,您将获得一个函数,将Either a Char
转换为either error flipNucleotide
,右侧的情况为翻转的字符,左侧的情况会导致错误。
当然{{1}}是更简单的解决方案。
答案 2 :(得分:0)
这并不能完全解决您的类型错误,但我建议您重新考虑您的代表。具体而言,通常最好使用类型来强制执行不变量,避免可能抛出错误或异常的部分函数,并避免意外混淆可能属于代码的不同部分的相关事物。有很多方法可以解决这个问题,但这是一个方法。这种方法假装DNA和RNA具有完全不同种类的核苷酸。在化学上,这不是真的,但它可能是你正在做的事情的合理代表。实际上,对化学现实进行编码可能超出了Haskell类型系统的能力,并且在这种情况下捕获错误实际上可能没那么有用。
data DNANucleotide = GD | CD | TD | AD
data RNANucleotide = GR | CR | UR | AR
toStringDNA :: [DNANucleotide] -> String
toStringDNA = map (\nucleotide -> case nucleotide of
{GD -> 'G'; CD -> 'C'; TD -> 'T'; AD -> 'A'})
toStringRNA = ...
fromCharDNA :: Char -> Maybe DNANucleotide
fromCharDNA 'G' = Just GD
fromCharDNA 'C' = Just CD
...
fromCharDNA _ = Nothing
fromCharRNA = ...
fromStringDNA :: String -> Maybe [DNANucleotide]
fromStringDNA = mapM fromCharDNA
fromStringRNA :: String -> Maybe [RNANucleotide]
fromStringRNA = mapM fromCharRNA
一旦你使用DNA和RNA进入工作的实际机制,而不是从字符串中读取它们,就不会再有错误了:
transcribeN :: DNANucleotide -> RNANucleotide
transcribeN GD = CR
transcribeN CD = GR
transcribeN TD = AR
transcribeN AD = UR
transcribe :: [DNANucleotide] -> [RNANucleotide]
transcribe = map transcribeN