تحويل نوع مثيلات الأسرة إلى كثافة العمليات
-
14-12-2019 - |
سؤال
لدي هذا الرمز:
type family Id obj :: *
type instance Id Box = 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
وبالتالي, كيف يمكنني إنشاء تحويل لمعرف نوع الأسرة للحصول على كثافة العمليات?
استنادا إلى الجواب من قبل إهيرد ، حاولت ما يلي لكنه لا يعمل إما:
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
لاستخدام.تعد عائلات البيانات أحد الحلول ، ولكن البديل الذي يسهل التعامل معه في بعض الأحيان هو استخدام شاهد من النوع.
تغيير صفك إلى
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
يجب أن تظهر مع?وهذا هو ، هل هناك واحد إلى واحد المراسلات بين a
ق و Id a
س?إذا كان الأمر كذلك ، فإن عائلات البيانات هي النهج الصحيح.إذا لم يكن كذلك ، قد تفضل الشاهد.
نصائح أخرى
لا يمكنك ذلك.ستحتاج إلى testFunc :: (IdToInt a) => Id a -> Int
.نوع الأسر مفتوحة، لذلك يمكن لأي شخص أن يعلن
في أي وقت، ولا تقدم أي وظيفة تحويل.أفضل شيء يجب القيام به هو وضع عائلة النوع في الفصل:
giveacodicetagpre.ستظل بحاجة إلى السياق، على الرغم من.
لا يمكنك استخدام دالة من النوع 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
الوظائف التي لها نوع Int -> Int
, ، لذلك لا يمكن لمدقق النوع تحديد أيهما يستخدم.
يجب عليك استخدام عائلة بيانات بدلا من عائلة النوع ، وإعلان مثيلات النوع الجديد.
data family Id a :: *
newtype instance Id () = IdUnit Int
newtype instance Id Char = IdChar Int
مع مثيل نوع جديد, Id ()
و Id Char
كلاهما إنتس ، ولكن لديهم أنواع مختلفة.نوع من Id
يبلغ المدقق نوع التي مثقلة وظيفة للاستخدام.