在Haskell monad链中键入检查错误

kks*_*eed 4 monads haskell map

我是Haskell的新手,我只是关注RWH的例子.我在第14章遇到以下程序的问题:

import qualified Data.Map as M

type PersonName = String
type PhoneNumber = String
type BillingAddress = String
data MobileCarrier = Honest_Bobs_Phone_Network
                   | Morrisas_Marvelous_Mobiles
                   | Petes_Plutocratic_Phones
                     deriving (Eq, Ord)

findCarrierBillingAddress :: PersonName
                          -> M.Map PersonName PhoneNumber
                          -> M.Map PhoneNumber MobileCarrier
                          -> M.Map MobileCarrier BillingAddress
                          -> Maybe BillingAddress
-- This will work
findCarrierBillingAddress person phoneMap carrierMap addressMap = do
  phone <- M.lookup person phoneMap
  carrier <- M.lookup phone carrierMap
  address <- M.lookup carrier addressMap
  return address

-- This will NOT work:
findCarrierBillingAddress person phoneMap carrierMap addressMap =
    return person >>=
    lookup phoneMap >>=
    lookup carrierMap >>=
    lookup addressMap
  where lookup = flip M.lookup
Run Code Online (Sandbox Code Playgroud)

似乎在使用>> =以monad链接格式编写findCarrierBillingAddres时,它只是不键入check:

/home/bruce/Programming/haskell/real/ch14/hello.hs:21:9:
    Couldn't match type `[Char]' with `MobileCarrier'
    Expected type: MobileCarrier -> Maybe BillingAddress
      Actual type: PersonName -> Maybe BillingAddress
    In the return type of a call of `lookup'
    In the second argument of `(>>=)', namely `lookup addressMap'
    In the expression:
      return person >>= lookup phoneMap >>= lookup carrierMap
      >>= lookup addressMap

/home/bruce/Programming/haskell/real/ch14/hello.hs:21:16:
    Couldn't match type `MobileCarrier' with `[Char]'
    Expected type: M.Map PersonName BillingAddress
      Actual type: M.Map MobileCarrier BillingAddress
    In the first argument of `lookup', namely `addressMap'
    In the second argument of `(>>=)', namely `lookup addressMap'
    In the expression:
      return person >>= lookup phoneMap >>= lookup carrierMap
      >>= lookup addressMap
Failed, modules loaded: none.
Run Code Online (Sandbox Code Playgroud)

问题是..为什么使用>> =的第二种格式不会进行类型检查?

Dan*_*zer 8

这只是单态性限制再次起作用.由于您具有没有类型签名的模式绑定,因此推断类型是单态的,因此由第一次使用确定.

只需将其更改为

lookup m k = flip M.lookup m k
Run Code Online (Sandbox Code Playgroud)

甚至

lookup m = flip M.lookup m
Run Code Online (Sandbox Code Playgroud)

你只需要说服GHC推广它只是一个简单的模式绑定时不会做的功能.添加参数会将其转换为函数绑定,这意味着它将完全一般化.

如果我在那里失去了一些你,我在博客上写了这个


sab*_*uma 7

当类型推断器试图推导出本地函数的类型时, 您会遇到单态限制lookup.

它不是推断出最一般的类型,而是从第一次使用它确定lookup它应该具有类型lookup :: Map [Char] [Char] -> [Char] -> Maybe [Char],当你尝试在它的值上使用它时,它不能统一Map [Char] MobileCarrier.

您的第一个选择是使用pragma禁用单同性限制 {-# LANGUAGE NoMonomorphismRestriction #-}.第二个选项是一个类型签名添加到lookup为使

findCarrierBillingAddress person phoneMap carrierMap addressMap =
    return person >>=
    lookup phoneMap >>=
    lookup carrierMap >>=
    lookup addressMap
  where lookup :: Ord k => M.Map k a -> k -> Maybe a
        lookup = flip M.lookup
Run Code Online (Sandbox Code Playgroud)