タイプファミリインスタンスをINTに変換します
-
14-12-2019 - |
質問
このコードを持っています:
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つだけ表示されますか?つまり、a
sとId a
sの間に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
の種類は、idToInt
とInt -> 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 -> Int
とId ()
はどちらもintsですが、異なるタイプがあります。Id Char
のタイプは、オーバーロードされた機能を使用するタイプチェッカーに通知します。