Consider the following type constructor:
newtype Mapnad k v = Mapnad { runMapnad :: Map [k] v }
Since Ord k => Ord [k]
(lexicographical order), we can reuse the functor instance for maps for this type in an obvious way:
deriving instance Ord k => Functor (Mapnad k)
Furthermore, it seems as though Ord k => Monad (Mapnad k)
, according to the following scheme:
-- For readability
type (×) = (,)
infixr ×
toList' :: Ord k => Mapnad k v -> [[k] × v]
fromList' :: Ord k => [[k] × v] -> Mapnad k v
return' :: Ord k => a -> Mapnad k a
return' = fromList' . return . return
join' :: Ord k => Mapnad k (Mapnad k v) -> Mapnad k v
join' =
fmap toList' -- Mapnad k [[k] × v]
>>> toList' -- [[k] × [[k] × v]]
>>> (=<<) sequenceA -- [[k] × [k] × v]
>>> fmap join -- [[k] × v]
>>> fromList' -- Mapnad k v
-- Note: we are using the writer monad for tuples above
instance Ord k => Applicative (Mapnad k)
where
pure = return
(<*>) = ap
instance Ord k => Monad (Mapnad k)
where
return = return'
ma >>= amb = join' $ fmap amb ma
Is this a legal monad instance? QuickCheck seems to suggest so, but it'd be good to know for sure one way or the other.
Bonus question: Assuming this is indeed a monad, are there any monoids k
besides the free [a]
monoid for which Map k
is a monad? There are certainly counterexamples: i.e. monoids k
for which Map k
is not a monad. For instance, with the same monad instance for Map (Sum Int)
, QuickCheck finds a counterexample to the associativity law.
-- m >>= (\x -> k x >>= h) == m >>= k >>= h
m :: { 0 -> 0; 3 -> 7 }
k :: \x -> if (odd x) then { -3 -> 1 } else { 0 -> 0 }
h :: \x -> if (odd x) then { } else { 0 -> 0 }
Map
is just like a function, so it should be easy to make it a modified version of aReader
monad. In your example, it looks like you are reimplementingListT (Writer k) v
. – Ruifeng XieMap
is just like a function, at least not without changes to the notion of a function like imposing bounds on the domain and partializing the codomain. RegardingListT
, which definition ofListT
are you referring to? – Asad Saeeduddintype Map k v = k -> Maybe v
which isReaderT k Maybe v
, so semantically there is a legit monad instance. Probably not very useful, though -return
produces an infinite map, andjoin
diagonalises a 2D map. Not really an API which most bread-and-butterData.Map
users will be clamouring for! – Benjamin HodgsonBind
instance forMap
in semigroupoids. – duplodeContT r (Map k)
surprisingly is a monad, and can do something kind of similar to what you're talking about here. – Jeremy List