使用: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变形金刚。
答案 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
),因为您无法从范围之间删除元素。
你无法使其成为Apply
或Applicative
,因为无法制定合法的Monad
/ pure
,它应该在所有键上都有值。或者,如果某个return
类型类约束Finite
(因为k
在其主干中是严格的,因此您无法创建无限的地图)。
编辑:在评论中指出。如果我们正确思考,上面尝试用Map
对MaybeT (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
成为Monad
。join
。它如何直观地工作?我们会映射地图的值,为每个地图返回一个新地图,然后使用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
/ mapMaybeWithkey
和foldMap
/ foldMapWithKey
。
答案 2 :(得分:0)
您如何为return
实施Data.Map
?据推测return x
x
会有{{1}}作为值,但是有哪些键?