عائلات من نوع هاسكل والحجج الوهمية
-
19-09-2019 - |
سؤال
لقد قمت بإنشاء وظيفة مشابهة لـ numpy's array
.يقوم بتحويل القوائم إلى صفائف، وقوائم القوائم إلى صفائف ثنائية الأبعاد، وما إلى ذلك.
يعمل مثل هذا:
ghci> arrFromNestedLists ["hello", "world"] :: Array (Int, (Int, ())) Char
array ((0,(0,())),(1,(4,()))) [((0,(0,())),'h'),((0,(1,())),'e'),((0,(2,())),'l'),((0,(3,())),'l'),((0,(4,())),'o'),((1,(0,())),'w'),((1,(1,())),'o'),((1,(2,())),'r'),((1,(3,())),'l'),((1,(4,())),'d')]
(Int, (Int, ()))
و لا (Int, Int)
لأنني لا أعرف طريقة برمجية لزيادة طول الصف.(سؤال جانبي:هل هناك مثل هذه الطريقة؟)
كان ترميزه محرجًا واضطررت إلى القيام "بحل بديل" (تمرير الوسائط الوهمية إلى الوظائف) حتى يعمل.وأتساءل عما إذا كان هناك طريقة أفضل.
إذن، هذا هو الكود، الذي تمت مقاطعته بتفاصيل الحلول القبيحة:
{-# LANGUAGE FlexibleInstances, ScopedTypeVariables, TypeFamilies #-}
type family ListOfIndex i a
type instance ListOfIndex () a = a
type instance ListOfIndex (Int, i) a = [ListOfIndex i a]
class Ix i => ArrConv i where
acBounds :: a -> ListOfIndex i a -> (i, i)
acFlatten :: i -> ListOfIndex i a -> [a]
acBounds
"يجب ان يكون :: ListOfIndex i a -> (i, i)
.ومثل ذلك ل acFlatten
.يتم إعطاء كل متغير وهمي (undefined
هي دائمًا القيمة المعطاة) لأنه بخلاف ذلك لم أتمكن من تجميعها :(
arrFromNestedLists :: forall i a. ArrConv i => ListOfIndex i a -> Array i a
arrFromNestedLists lst =
listArray
(acBounds (undefined :: a) lst)
(acFlatten (undefined :: i) lst)
أعلاه هو الدمية undefined
حجة تمر في العمل.يخبر GHC أي مثيل ListOfIndex
ليستخدم.
instance ArrConv () where
acBounds _ = const ((), ())
acFlatten _ = (: [])
يجب أن تكون الوظيفة أدناه هي acBounds
وظيفة في مثيل ArrConv
, ، وتم إعلانه بالخارج فقط لأنني بحاجة إلى استخدامه ScopedTypeVariables
ولا أعرف كيف يمكنني القيام بذلك في وظيفة في تعريف مثيل ..
acSucBounds
:: forall a i. ArrConv i
=> a -> [ListOfIndex i a] -> ((Int, i), (Int, i))
acSucBounds _ lst =
((0, inStart), (length lst - 1, inEnd))
where
(inStart, inEnd) = acBounds (undefined :: a) (head lst)
instance ArrConv i => ArrConv (Int, i) where
acBounds = acSucBounds
acFlatten _ = concatMap (acFlatten (undefined :: i))
المحلول
السبب في أن الحجج الإضافية إلى ACBOUNDS و ACFLATTEN ضرورية هي أن الأنواع a
و i
لا يمكن استردادها من ListOfIndex i a -> (i, i)
و ListOfIndex i a -> [a]
على التوالى. حل واحد هو الجمع بين الطريقتين في طريقة واحدة acArgs
من النوع ListOfIndex i a -> ((i, i), a)
. وبعد الآن المشكلة الوحيدة هي استخدامها في مثيل (Int, i)
بطريقة تمنع typechecker من تعميم نوعها مما تسبب في نفس المشكلة كما كان من قبل (على سبيل المثال، لا يمكننا استخدامها ببساطة fst . acArgs
).
} : ListOfIndex IA -> ((I، I)، [A]) مثيل arrcrconv () حيث أكارات X = (((()، ())، [x]) مثيل arrcrconv i => arrcrconv (int، i) حيث أكارات LST = (((0، Instart)، (طول LST - 1، Inend))، Args >> = SND) حيث Args = Map Acargs Lst (Instart، Innend) = FST (Head Args) Arrfromnestedlists :: Arrconv I => ListOfIndex IA -> صفيف IA ArrFromnestfedlists = Listray Uncurry Listray. أكارات
نصائح أخرى
إذا كنت تريد الاحتفاظ بها acBounds
و acFlatten
منفصلة، يمكنك إضافة وسيطة العلامة على مستوى النوع إليه، أي. acBounds
سيكون له نوع acBounds :: Proxy a -> ListOfIndex i a -> (i, i)
.وهذا يلغي الحاجة إلى undefined
الحجج، حيث يمكنك تمرير فقط (Proxy :: SomeConcreteType)
إليها؛و acBounds
ليس لديه طريقة لاستخراج أي معلومات مفيدة على مستوى القيمة منه، لأنه متماثل (بطريقة غير مكتوبة) لنوع الوحدة.