没有Monad实例的“Data.Map”,但是Scala的地图?

时间:2015-07-16 20:00:12

标签: scala haskell

使用:i Map,我没有看到Monad实例。

ghci> import Data.Map
ghci> :i Map
type role Map nominal representational
data Map k a
  = containers-0.5.5.1:Data.Map.Base.Bin {-# UNPACK #-} !containers-0.5.5.1:Data.Map.Base.Size
                                         !k
                                         a
                                         !(Map k a)
                                         !(Map k a)
  | containers-0.5.5.1:Data.Map.Base.Tip
    -- Defined in ‘containers-0.5.5.1:Data.Map.Base’
instance (Eq k, Eq a) => Eq (Map k a)
  -- Defined in ‘containers-0.5.5.1:Data.Map.Base’
instance Functor (Map k)
  -- Defined in ‘containers-0.5.5.1:Data.Map.Base’
instance (Ord k, Ord v) => Ord (Map k v)
  -- Defined in ‘containers-0.5.5.1:Data.Map.Base’
instance (Ord k, Read k, Read e) => Read (Map k e)
  -- Defined in ‘containers-0.5.5.1:Data.Map.Base’
instance (Show k, Show a) => Show (Map k a)
  -- Defined in ‘containers-0.5.5.1:Data.Map.Base

但是,我看到Scala的Map实现了flatMap

我不知道Map是否遵守Monad法律。

如果我对Data.Map的观察是正确的,那么为什么Haskell中没有instance Monad (Map)

我查看了这个answer,但看起来它使用的是Monad变形金刚。

3 个答案:

答案 0 :(得分:5)

很难说出Scala的flatMap应该做什么:

trait Map[A, B+] extends Iterable[(A, B)] {
  def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): Map[B]
}

它需要一个键,值对地图(因为flatMap来自Iterable,其中A(A,B)):

scala> val m = Map("one" -> 1, "two" -> 2)
m: scala.collection.immutable.Map[String,Int] = Map(one -> 1, two -> 2)

scala> m.flatMap (p => p match { case (_, v) => List(v, v + 3) })
res1: scala.collection.immutable.Iterable[Int] = List(1, 4, 2, 5)

这不是monadic bind,它更接近Foldable的{​​{1}}

foldMap

λ > import Data.Map λ > import Data.Monoid λ > import Data.Foldable λ > let m = fromList [("one", 1), ("two", 2)] λ > (\v -> [v, v + 3]) `foldMap` m [1,4,2,5] 合法Ord k => Apply (Map k v)Ord k => Bind (Map k v)

Map

有点像-- | A Map is not 'Applicative', but it is an instance of 'Apply' instance Ord k => Apply (Map k) where (<.>) = Map.intersectionWith id (<. ) = Map.intersectionWith const ( .>) = Map.intersectionWith (const id) -- | A 'Map' is not a 'Monad', but it is an instance of 'Bind' instance Ord k => Bind (Map k) where m >>- f = Map.mapMaybeWithKey (\k -> Map.lookup k . f) m 实例,按键按压缩元素。 注意: ZipList不是ZipList(仅Bind),因为您无法从范围之间删除元素。

你无法使其成为ApplyApplicative,因为无法制定合法的Monad / pure,它应该在所有键上都有值。或者,如果某个return类型类约束Finite(因为k在其主干中是严格的,因此您无法创建无限的地图)。

编辑:在评论中指出。如果我们正确思考,上面尝试用MapMaybeT (Reader k) v = k -> Maybe v进行具体的(可检查的)表示。但是我们失败了,因为我们无法代表Map k v。但我们可以通过明确表示这种情况来尝试这样做:

pure x = const x

确实有效!然而,这个有点可疑的定义,因为我们无法定义语义正确的module MMap (main) where import Data.Map (Map) import qualified Data.Map as Map import Test.QuickCheck import Test.QuickCheck.Function import Control.Applicative import Control.Monad -- [[ MMap k v ]] ≅ k -> Maybe v data MMap k v = MConstant v | MPartial (Map k v) deriving (Eq, Ord, Show) -- Morphism lookup :: Ord k => k -> MMap k v -> Maybe v lookup _ (MConstant x) = Just x lookup k (MPartial m) = Map.lookup k m instance Functor (MMap k) where fmap f (MConstant v) = MConstant (f v) fmap f (MPartial m) = MPartial (fmap f m) instance Ord k => Applicative (MMap k) where pure = MConstant (MConstant f) <*> (MConstant x) = MConstant (f x) (MConstant f) <*> (MPartial x) = MPartial (fmap f x) (MPartial f) <*> (MConstant x) = MPartial (fmap ($x) f) (MPartial f) <*> (MPartial x) = MPartial (Map.intersectionWith ($) f x) instance Ord k => Monad (MMap k) where return = MConstant (MConstant x) >>= f = f x (MPartial m) >>= f = MPartial $ Map.mapMaybeWithKey (\k -> MMap.lookup k . f) m instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (MMap k v) where arbitrary = oneof [ MConstant <$> arbitrary , MPartial . Map.fromList <$> arbitrary ] prop1 :: Int -> Fun Int (MMap Int Int) -> Property prop1 x (Fun _ f) = (return x >>= f) === f x prop2 :: MMap Int Int -> Property prop2 x = (x >>= return) === x prop3 :: MMap Int Int -> Fun Int (MMap Int Int) -> Fun Int (MMap Int Int) -> Property prop3 m (Fun _ f) (Fun _ g) = ((m >>= f) >>= g) === (m >>= (\x -> f x >>= g)) main :: IO () main = do quickCheck prop1 quickCheck prop2 quickCheck prop3 实例:

Eq

m1 = MConstant 'a' m2 = MPartial (Map.fromList [(True, 'a'), (False, 'a')]) m1在语义上是等效的(m2具有相同的结果),但结构上不同。我们无法知道lookup k何时定义了所有键值。

Spine指的是,呃,数据结构脊柱。例如,列表定义为

MPartial

脊柱不严格,但

data List a = Nil | Cons a (List a)

您可以定义无限data SList a = SNil | SCons a !(SList a) ,但List s:

SList

作为Map is also strict in it's spine

λ Prelude > let l = Cons 'a' l
λ Prelude > let sl = SCons 'a' sl
λ Prelude > l `seq` ()
()
λ Prelude > sl `seq` () -- goes into infinite loop

我们无法构建无限data Map k a = Bin {-# UNPACK #-} !Size !k a !(Map k a) !(Map k a) | Tip ,即使我们有获得Map类型的所有值的方法。但我们可以构造无限的普通Haskell列表:k[]制作pure

答案 1 :(得分:2)

不,确实Monad没有Map个实例。

  

我看到Scala的地图实现了flatMap

我假设您单独注意到doesn't make it a monad

但我们可以尝试让Haskell Map成为Monadjoin。它如何直观地工作?我们会映射地图的值,为每个地图返回一个新地图,然后使用unions将所有这些地图Map放在一起。这应该有效!

事实上,如果我们仔细研究import Data.Map import Data.Traversable import Data.Foldable import Data.Monoid 实现的类,我们会看到非常类似的东西:

Monoid.mconcat

其中unions扮演我们Traversable的角色,foldMapDefault提供的>>=完全符合我们的要求(可用于return )!

但是,当我们想要实施Map时,我们遇到了问题 - 没有关键!我们得到了一个值,但我们无法从中获得flatMap!通过使Monoid比monad更通用,Scala避免了同样的问题。我们可以通过获取密钥的默认值来解决此问题,例如:通过要求密钥类型为instance (Ord k, Monoid k) => Monad (Map k)实例,并使用return实例 - 但由于flatMap有限,它将无法满足monad法则。

但是,Scala中重载Map的所有用例都被Haskell <VirtualHost *:80> ServerName 127.0.0.1 DocumentRoot /var/www/errorpages/ </VirtualHost> <VirtualHost *:80> ServerName www.example.com ServerAlias *.example.com ServerAlias example.com ProxyRequests Off ProxyPreserveHost On ProxyPass / balancer://whatever/ ProxyPassReverse / balancer://whatever/ <Proxy balancer://whatever/> ProxySet failonstatus=500 BalancerMember http://127.0.0.1:42 retry=1 BalancerMember http://127.0.0.1:80 status=+H </Proxy> </VirtualHost> 上的等效方法所涵盖。您希望仔细查看mapMaybe / mapMaybeWithkeyfoldMap / foldMapWithKey

答案 2 :(得分:0)

您如何为return实施Data.Map?据推测return x x会有{{1}}作为值,但是有哪些键?