3

m次のような型アノテーションを使用してファンクター風の操作を提供するクラスを定義したいと思います。

mapify ::(a-> 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!!!

それがうまくいかない理由を理解しています。私が思いついた解決策は、それを2つのクラスに分割することでした。つまり、「通常の」クラスと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

それはうまくいきますが、もっと良い方法はありますか?


編集:私は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.
4

2 に答える 2

6

1つの可能性は、別の関連するタイプを使用して「コンテナ」タイプをエンコードすることです。

import qualified Data.Map.Lazy as M

class MyMap m where
  type Key m
  type Value m
  type Container m :: * -> *
  keys :: m -> [Key m]
  elems :: m -> [Value m]
  mapify :: (a -> b) -> Container m a -> Container m b

instance MyMap (M.Map k v) where
  type Key (M.Map k v) = k
  type Value (M.Map k v) = v
  type Container (M.Map k v) = M.Map k
  keys = M.keys
  elems = M.elems
  mapify = M.map

Containerfor aMapはであるという考えなので、を関連付けられたキータイプとMap kバンドルします 。Mapそうすればmapify、関数は関数をコンテナーに持ち上げます。「より良い」かどうかはあなた次第だと思いますが、型クラスの数を減らすことができます。は標準の型クラスと同じであるMyMapFため、例のクラスは必要ありません。MyMapFFunctor

mapify 了解しました。そのエラーは、の定義を少し変更することで修正できます。

class MyMap m where
  type Key m
  type Value m
  type Container m :: * -> *
  keys :: m -> [Key m]
  elems :: m -> [Value m]
  -- mapify :: (a -> b) -> Container m a -> Container m b

  -- Make sure the type-checker knows that m2 is just the container of m with
  -- a different value
  mapify :: (MyMap m2, m2 ~ Container m (Value m2)) => (Value m -> Value m2) -> m -> m2


instance MyMap (M.Map k v) where
  type Key (M.Map k v) = k
  type Value (M.Map k v) = v
  type Container (M.Map k v) = M.Map k
  keys = M.keys
  elems = M.elems
  mapify = M.map

doSomething
  :: (MyMap m1, MyMap m2, m2 ~ Container m1 (Value m2)) =>
     (Value m1 -> Value m2) -> m1 -> m2
doSomething f m = mapify f m

これはタイプチェックします。Value問題は、型チェッカーが、基になるコンテナーを変更せずに、MyMap インスタンスのを変更するだけであるというより強力なヒントを必要としていることだと思います。

于 2013-03-01T16:31:15.380 に答える
2

この質問は少し古いと思いますが、特にsabaumaが提案した質問よりも少し単純に見えるので、関係のないものを探して、あなたが好きかもしれないアイデアを見つけました。

import qualified Data.Map.Lazy as M

class MyMap m where
  type Key m
  keys :: m a -> [Key m]
  elems :: m a -> [a]
  mapify :: (a -> b) -> m a -> m b

instance MyMap (M.Map k) where
  type Key (M.Map k) = k
  keys = M.keys
  elems = M.elems
  mapify = M.map

キーパッケージも参照してください。

于 2014-05-06T01:09:33.517 に答える