6

私は HList の実装に取り​​組んでおり、そのmap関数を実装しようとして立ち往生しています。私はさまざまなアプローチを試しましたが、それぞれのアプローチでその関数に関連するコンパイラ エラーが発生します。

以下は、ジェネリック関数Justを使用して入力データ構造のすべての要素に適用する方法の例です。

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

-- | An input heterogenous data structure
recursivePairs :: (Int, (Char, (Bool, ())))
recursivePairs = (1, ('a', (True, ())))

-- | This is how I want to use it
recursivePairs' :: (Maybe Int, (Maybe Char, (Maybe Bool, ())))
recursivePairs' = hMap Just recursivePairs

class HMap f input output where
  hMap :: f -> input -> output

-- | A counterpart of a Nil pattern match for a list
instance HMap f () () where
  hMap _ _ = ()

-- | A counterpart of a Cons pattern match for a list
instance 
  ( HMap f iTail oTail, 
    Apply f iHead oHead ) =>
  HMap f (iHead, iTail) (oHead, oTail) 
  where
    hMap f (head, tail) = (apply f head, hMap f tail)

class Apply f input output where
  apply :: f -> input -> output

instance Apply (input -> output) input output where
  apply = id

これにより、次のコンパイラ エラーが発生します。

No instance for (Apply (a0 -> Maybe a0) Int (Maybe Int))
  arising from a use of `hMap'
The type variable `a0' is ambiguous

これを解決する方法はまったくありますか?そうでない場合は、なぜですか?

4

2 に答える 2

5

問題は、さまざまな引数で多相関数を使用しようとしているが、Applyインスタンスが関数 (単一型) を取ることです。これは複数の方法で簡単に修正できます

data JustIfy = JustIfy
instance Apply JustIfy a (Maybe a) where
  apply _ = Just

recursivePairs' :: (Maybe Int, (Maybe Char, (Maybe Bool, ())))
recursivePairs' = hMap JustIfy recursivePairs

あなたのコードで問題なく動作します

EDIT:同じことへのより一般的なアプローチは(必須RankNTypes)です

--A "universal" action that works on all types
newtype Univ f = Univ (forall x. x -> f x)
instance Apply (Univ f) x (f x) where
   apply (Univ f) x = f x

recursivePairs' :: (Maybe Int, (Maybe Char, (Maybe Bool, ())))
recursivePairs' = hMap (Univ Just) recursivePairs

または、GHC の最近っぽいバージョンを使用していて、より多くの拡張機能を有効にしたい場合

newtype Univ' c f = Univ' (forall x. c x => x -> f x)
instance c x => Apply (Univ' c f) x (f x) where
  apply (Univ' f) x = f x

class All x
instance All x

recursivePairs' :: (Maybe Int, (Maybe Char, (Maybe Bool, ())))
recursivePairs' = hMap (Univ' Just :: Univ' All Maybe) recursivePairs

これは、マップする関数に「show」を含めるなどのことができるので、これは素晴らしいことです。

より一般的な解決策については、値レベルでコードを記述し、適切な型レベル プログラムを自動的に推論できるOleg の Type level lambda caclulusを確認してください。残念ながら、Oleg のソリューションは現時点ではかなり古く、私が特に気に入らない LC の名目上の実装を使用しています。私はどうすればもっとうまくやれるかを考えてきましたが、型族に決定的な平等がもたらされるまで延期するかもしれません。

私の見解では、最近の HList は、タプルではなく GADT と DataKinds を使用して作成する必要があります。型ファミリは機能依存性よりも望ましいですが、決定可能な同等性がないため、現在はより制限されています。

于 2013-04-06T23:07:18.953 に答える