为什么我必须用 mapM 组合 id
Why do I have to compose id with mapM
我有以下方法:
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
。接下来获取mapM nucleotideComplement
和id . mapM nucleotideComplement
的类型(:t
)时,好像是一样的。为什么我得到的效果如此不同?
希望有人能进一步澄清这一点。
我认为你读错了...
either error id . mapM nucleotideComplement
你似乎认为这意味着
either error (id . mapM nucleotideComplement)
实际上它的意思是
(either error id) . (mapM nucleotideComplement)
你没有在任何地方做 id . mapM nucleotideComplement
。您正在应用 mapM
,然后将结果传递给 either
,这将应用 error
或 id
,具体取决于它看到的是 Left
还是 Right
.
其中一个的类型是 (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
对于任何 monad m,char 是 "flipped" - 在这种情况下 m
是 Either String
。因此,通过组合这两个函数,您将得到一个将 Either String Char
转换为 Either a Char
的函数,其中右侧的情况是翻转的字符,左侧的情况导致错误。
当然 either error flipNucleotide
是更简单的解决方案。
这并不能完全解决您的类型错误,但我想建议您重新考虑您的陈述。具体来说,通常最好使用类型来强制执行不变量,避免可能抛出错误或异常的部分函数,并避免不小心混淆可能属于代码不同部分的相关事物。有多种方法可以解决这个问题,但这里有一个。这种方法假设 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
我有以下方法:
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
。接下来获取mapM nucleotideComplement
和id . mapM nucleotideComplement
的类型(:t
)时,好像是一样的。为什么我得到的效果如此不同?
希望有人能进一步澄清这一点。
我认为你读错了...
either error id . mapM nucleotideComplement
你似乎认为这意味着
either error (id . mapM nucleotideComplement)
实际上它的意思是
(either error id) . (mapM nucleotideComplement)
你没有在任何地方做 id . mapM nucleotideComplement
。您正在应用 mapM
,然后将结果传递给 either
,这将应用 error
或 id
,具体取决于它看到的是 Left
还是 Right
.
其中一个的类型是 (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
对于任何 monad m,char 是 "flipped" - 在这种情况下 m
是 Either String
。因此,通过组合这两个函数,您将得到一个将 Either String Char
转换为 Either a Char
的函数,其中右侧的情况是翻转的字符,左侧的情况导致错误。
当然 either error flipNucleotide
是更简单的解决方案。
这并不能完全解决您的类型错误,但我想建议您重新考虑您的陈述。具体来说,通常最好使用类型来强制执行不变量,避免可能抛出错误或异常的部分函数,并避免不小心混淆可能属于代码不同部分的相关事物。有多种方法可以解决这个问题,但这里有一个。这种方法假设 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