Я хочу, чтобы определить класс m
, который обеспечивает функтор-иш операции с сигнатуру типа, как это:Определение класса с функтора-иш и не функторных иш функций
mapify :: (а -> b) -> ma -> mb
Мне также нужны были некоторые другие операции без функтора. Я бы хотелось написать что-то вдоль линий:
class MyMap m where
type Key m
type Value m
keys :: m -> [Key m]
elems :: m -> [Value m]
mapify :: (a -> b) -> m a -> m b -- WON'T WORK!!!
Я понимаю, почему это не будет работать. Решение, с которым я столкнулся, состояло в том, чтобы разделить его на два класса - «нормальный», плюс один, построенный на Functor.
{-# LANGUAGE TypeFamilies #-}
import qualified Data.Map.Lazy as M
class MyMap m where
type Key m
type Value m
keys :: m -> [Key m]
elems :: m -> [Value m]
class MyMapF m where
mapify :: (a -> b) -> m a -> m b
instance MyMap (M.Map k v) where
type Key (M.Map k v) = k
type Value (M.Map k v) = v
keys = M.keys
elems = M.elems
instance MyMapF (M.Map k) where
mapify = M.map
Это хорошо работает, но есть ли лучший способ?
EDIT: Мне очень нравится решение, предлагаемое sabauma. Однако, когда я пытаюсь создать функцию, которая использует этот класс, я не могу заставить подпись типа работать.
doSomething
:: (MyMap m1, MyMap m2, Container m1 ~ Container m2) => -- line 22
(Value m1 -> Value m2) -> m1 -> m2 -- line 23
doSomething f m = mapify f m -- line 24
Ошибки я получаю:
../Amy3.hs:22:6:
Couldn't match type `b0' with `Value (Container m0 b0)'
`b0' is untouchable
inside the constraints (MyMap m1,
MyMap m2,
Container m1 ~ Container m2)
bound at the type signature for
doSomething :: (MyMap m1, MyMap m2, Container m1 ~ Container m2) =>
(Value m1 -> Value m2) -> m1 -> m2
Expected type: a0 -> b0
Actual type: Value m1 -> Value m2
../Amy3.hs:24:19:
Could not deduce (m2 ~ Container m0 b0)
from the context (MyMap m1, MyMap m2, Container m1 ~ Container m2)
bound by the type signature for
doSomething :: (MyMap m1, MyMap m2, Container m1 ~ Container m2) =>
(Value m1 -> Value m2) -> m1 -> m2
at ../Amy3.hs:(22,6)-(23,38)
`m2' is a rigid type variable bound by
the type signature for
doSomething :: (MyMap m1, MyMap m2, Container m1 ~ Container m2) =>
(Value m1 -> Value m2) -> m1 -> m2
at ../Amy3.hs:22:6
In the return type of a call of `mapify'
In the expression: mapify f m
In an equation for `doSomething': doSomething f m = mapify f m
../Amy3.hs:24:28:
Could not deduce (m1 ~ Container m0 a0)
from the context (MyMap m1, MyMap m2, Container m1 ~ Container m2)
bound by the type signature for
doSomething :: (MyMap m1, MyMap m2, Container m1 ~ Container m2) =>
(Value m1 -> Value m2) -> m1 -> m2
at ../Amy3.hs:(22,6)-(23,38)
`m1' is a rigid type variable bound by
the type signature for
doSomething :: (MyMap m1, MyMap m2, Container m1 ~ Container m2) =>
(Value m1 -> Value m2) -> m1 -> m2
at ../Amy3.hs:22:6
In the second argument of `mapify', namely `m'
In the expression: mapify f m
In an equation for `doSomething': doSomething f m = mapify f m
Failed, modules loaded: none.
Интересно, не работает ли 'класс Functor m => MyMap m где ...'? Таким образом, вы можете просто повторно использовать часть «Functor», и вам не нужно беспокоиться о 'm' vs' m a'. – Xeo