4

我是 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

似乎在使用 >>= 以 monad 链接格式编写 findCarrierBillingAddres 时,它只是不进行类型检查:

/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.

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

4

2 回答 2

8

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

只需将其更改为

lookup m k = flip M.lookup m k

甚至

lookup m = flip M.lookup m

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

如果我在上面失去了你一点,我已经写了关于这个的博客

于 2013-07-31T04:16:19.457 回答
7

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

它不是推导出最一般的类型,而是从第一次使用就确定lookup它应该具有 type 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
于 2013-07-31T04:13:48.393 回答