familles de type Haskell et des arguments factices
-
19-09-2019 - |
Question
J'ai fait une fonction similaire à la array
de numpy. Il convertit les listes de tableaux, listes de listes de tableaux 2d, etc.
Il fonctionne comme ceci:
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, ()))
et non (Int, Int)
parce que je ne sais pas d'une façon programatic d'augmenter la longueur d'un tuple. (Question de côté: est-il une telle façon)
Le codage de c'était maladroit et je devais faire une « solution » (passant autour des arguments factices aux fonctions) pour que cela fonctionne. Je me demande s'il y a une meilleure façon.
Alors, voici le code, interrompu par les détails des solutions de contournement laid:
{-# 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
"devrait" être :: ListOfIndex i a -> (i, i)
. Et de même pour acFlatten
. Chacun reçoit une variable factice (undefined
est toujours la valeur donnée), car sinon je ne pouvais pas le compiler: (
arrFromNestedLists :: forall i a. ArrConv i => ListOfIndex i a -> Array i a
arrFromNestedLists lst =
listArray
(acBounds (undefined :: a) lst)
(acFlatten (undefined :: i) lst)
Au-dessus est l'argument undefined
factice qui passe au travail. Il raconte l'GHC quelle instance de ListOfIndex
à utiliser.
instance ArrConv () where
acBounds _ = const ((), ())
acFlatten _ = (: [])
La fonction ci-dessous aurait dû être la fonction acBounds
dans une instance de ArrConv
, et est déclarée en dehors seulement parce que je dois utiliser ScopedTypeVariables
et je ne sais pas comment je peux le faire en fonction dans une définition d'exemple ..
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))
La solution
La raison pour laquelle les arguments supplémentaires à acBounds et acFlatten sont nécessaires est que les types a
et i
ne peuvent pas être récupérés à partir ListOfIndex i a -> (i, i)
et ListOfIndex i a -> [a]
respectivement. Une solution consiste à combiner les deux méthodes dans une méthode de type acArgs
de ListOfIndex i a -> ((i, i), a)
. Maintenant, le seul problème est de l'utiliser dans l'instance de (Int, i)
d'une manière qui empêche la typechecker de son type trop généralisant causant beaucoup le même problème que précédemment (par exemple, nous ne pouvons pas utiliser simplement fst . acArgs
).
{-# LANGUAGE TypeFamilies, FlexibleInstances #-} import Data.Array 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 acArgs :: ListOfIndex i a -> ((i, i), [a]) instance ArrConv () where acArgs x = (((), ()), [x]) instance ArrConv i => ArrConv (Int, i) where acArgs lst = (((0, inStart), (length lst - 1, inEnd)), args >>= snd) where args = map acArgs lst (inStart, inEnd) = fst (head args) arrFromNestedLists :: ArrConv i => ListOfIndex i a -> Array i a arrFromNestedLists = uncurry listArray . acArgs
Autres conseils
Si vous voulez garder acBounds
et acFlatten
séparé, vous pouvez ajouter un argument tag niveau de type à elle, à savoir acBounds
aurait le type acBounds :: Proxy a -> ListOfIndex i a -> (i, i)
. Cela supprime la nécessité pour les arguments de undefined
, puisque vous pouvez juste passer (Proxy :: SomeConcreteType)
à elle; et acBounds
n'a aucun moyen d'extraction des informations de valeur de niveau utile, puisqu'il est isomorphe (d'une manière non typée) pour le type d'unité.