Семейства типов Haskell и фиктивные аргументы

StackOverflow https://stackoverflow.com/questions/2043610

  •  19-09-2019
  •  | 
  •  

Вопрос

Я сделал функцию, похожую на numpy array.Он преобразует списки в массивы, списки списков в 2d-массивы и т. д.

Это работает следующим образом:

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) таким образом, чтобы средство проверки типов не слишком обобщало свой тип, вызывая ту же проблему, что и раньше (например, мы не можем просто использовать 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

Другие советы

Если вы хотите сохранить acBounds и acFlatten отдельно, вы можете добавить аргумент тега уровня типа к этому, т.е. acBounds будет иметь тип acBounds :: Proxy a -> ListOfIndex i a -> (i, i).Это устраняет необходимость в undefined аргументы, так как вы можете просто передать (Proxy :: SomeConcreteType) к этому;и acBounds не имеет возможности извлечь из него какую-либо полезную информацию на уровне значений, поскольку он изоморфен (нетипизированным образом) типу модуля.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top