Существует ли гибкий способ указания типов возврата для семейств типов Хаскелла?

Недавно я начал использовать библиотеку Repa на Haskell, которая сильно зависит от семейств типов и связанных типов. Я могу определить массив Repa следующим образом:

ghci> let x = fromListUnboxed (Z :. (5 :: Int) :. (2 :: Int)) [1..10]

и оперируй на нем так:

ghci> computeP $ R.map id x :: IO (Array U DIM2 Double)
AUnboxed ((Z :. 5) :. 2) (fromList [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0])

где U - связанный тип данных, который приведет к тому, что результат будет представлен как распакованный массив; DIM2 - это размерность массива. Мне не нравится, что я должен указать конкретное измерение, даже если оно может быть выведено. Вместо этого я хотел бы написать что-то вроде этого:

ghci> computeP $ R.map id x :: Shape sh => IO (Array U sh Double)

Это недопустимо, но я хочу указать тип массива, передав соответствующий соответствующий тип данных (в этом примере U), но оставив форму без изменений. Возможно ли что-то подобное?

2 ответа

Решение

Работает ли что-то подобное?

asArrayU :: Array U sh a -> Array U sh a
asArrayU = id

computeP $ asArrayU <$> R.map id x

Ваш пример

ghci> computeP $ R.map id x :: Shape sh => IO (Array U sh Double)

не работает, потому что здесь вы объявляете, что полученное значение является полиморфным и может иметь любую возможную форму, но это явно не соответствует действительности x имеет определенную форму, и, следовательно, результат может иметь только ту же конкретную форму, а именно DIM2,

Что вы можете сказать, что сочетание computeP а также R.map id сохраняет ту же форму, независимо от того, какая форма изначально. Т.е.

ghci> (computeP . R.map id :: Shape sh => Array U sh Double -> IO (Array U sh Double)) x
AUnboxed ((Z :. 5) :. 2) (fromList [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0])

Важной частью здесь является то, что мы фиксируем представление для неупакованных значений, потому что это та часть, которая не может быть автоматически определена компилятором.

Но, не зная больше о вашем реальном сценарии использования, сложно сказать, какой вариант лучше всего синтаксически выразить то, что вы хотите. Вы в основном заинтересованы в тестировании вещей в GHCi без указания явных типов или вы хотите избежать явной типизации где-то в реальной программе?

Например, вы можете определить что-то вроде

computeMap :: (Unbox a, Unbox b, Shape sh) => (a -> b) -> Array U sh a -> IO (Array U sh b)
computeMap f = computeP . R.map f

и тогда вы можете сказать

ghci> computeMap id x
AUnboxed ((Z :. 5) :. 2) (fromList [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0])
Другие вопросы по тегам