質問

このコードを持っています:

type family Id obj :: *
type instance Id Box = Int
.

そして私はそれを常にIDタイプの家族から常にintを取得することができます。変換が必要になることを認識しています。

私は多分クラスを作ることになると思いました:

class IdToInt a where
  idToInt :: Id a -> Int

instance IdToInt Box where
  idToInt s = s
.

そしてそれは実際にコンパイルします。しかし私がそれを使おうとすると:

testFunc :: Id a -> Int
testFunc x = idToInt x
.

私はエラーを得ます:

src/Snowfall/Spatial.hs:29:22:
Couldn't match type `Id a0' with `Id a'
NB: `Id' is a type function, and may not be injective
In the first argument of `idToInt', namely `x'
In the expression: idToInt x
In an equation for `testFunc': testFunc x = idToInt x
.

SO、タイプファミリIDの変換を作成するにはどうすればよいですか?

Ehirdによる答えに基づいて、私は次のようにしましたが、どちらも機能しません:

class IdStuff a where
  type Id a :: *
  idToInt :: Id a -> Int

instance IdStuff Box where
  type Id Box = Int
  idToInt s = s

testFunc :: (IdStuff a) => Id a -> Int
testFunc x = idToInt x
.

エラーを与える:

src/Snowfall/Spatial.hs:45:22:
Could not deduce (Id a0 ~ Id a)
from the context (IdStuff a)
  bound by the type signature for
             testFunc :: IdStuff a => Id a -> Int
  at src/Snowfall/Spatial.hs:45:1-22
NB: `Id' is a type function, and may not be injective
In the first argument of `idToInt', namely `x'
In the expression: idToInt x
In an equation for `testFunc': testFunc x = idToInt x
.

役に立ちましたか?

解決

他のものが指摘したように、問題はコンパイラがどのaを使用するかを理解することができないことです。データファミリは1つの解決策ですが、時々取り扱いが簡単な代替案はタイプの証人を使用することです。

クラスを

に変更する
class IdToInt a where
  idToInt :: a -> Id a -> Int

instance IdToInt Box where
  idToInt _ s = s

-- if you use this a lot, it's sometimes useful to create type witnesses to use
box = undefined :: Box

-- you can use it like
idToInt box someId

-- or
idToInt someBox (getId someBox)
.

回答する必要がある質問は、任意のIdの場合、aが1つだけ表示されますか?つまり、asとId asの間に1対1の対応がありますか?もしそうなら、データファミリは正しいアプローチです。そうでなければ、あなたは証人を好むかもしれません。

他のヒント

できません。testFunc :: (IdToInt a) => Id a -> Intが必要です。タイプファミリーが開いているので、誰でも宣言できます

type instance Id Blah = ()
.

いつでも変換機能を提供しません。やるべきことは、タイプ家族をクラスに入れることです。

class HasId a where
  type Id a
  idToInt :: Id a -> Int

instance IdToInt Box where
  type Id Box = Int
  idToInt s = s
.

まだコンテキストを必要とします。

IdToInt a => Id a -> Intの型があるのかを判断する方法がないため、aの機能を使用することはできません。次の例はこれを示しています。

type family Id a :: *
type instance Id () = Int
type instance Id Char = Int

class IdToInt a where idToInt :: Id a -> Int

instance IdToInt () where idToInt x = x + 1
instance IdToInt Char where idToInt x = x - 1

main = print $ idToInt 1
.

上記のコンテキストのId () = Id Char = Intの種類は、idToIntInt -> Intに等しいId () -> Intです。オーバーロードされたメソッドがタイプに基づいて選択されることを忘れないでください。どちらのクラスインスタンスはId Char -> Int型を持つidToInt関数を定義しているため、タイプチェッカーはどちらを使用するかを決めることができません。

型ファミリの代わりにデータファミリを使用し、NewTypeインスタンスを宣言する必要があります。

data family Id a :: *
newtype instance Id () = IdUnit Int
newtype instance Id Char = IdChar Int
.

NewTypeインスタンスでは、Int -> IntId ()はどちらもintsですが、異なるタイプがあります。Id Charのタイプは、オーバーロードされた機能を使用するタイプチェッカーに通知します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top