Есть ли гибкий способ указания возвращаемых типов для семейств типов Haskell?

Недавно я начал использовать библиотеку Haskell Repa, которая сильно зависит от type семейства и связанные типы. Я могу определить массив 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 в этом примере), но оставить форму без изменений. Возможно ли что-то подобное?


person Jan Stolarek    schedule 10.10.2012    source источник


Ответы (2)


Что-то вроде этого работает?

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

computeP $ asArrayU <$> R.map id x
person Sjoerd Visscher    schedule 10.10.2012
comment
Нет, но это так: asArrayU ‹$› (computeP $ R.map id x) Спасибо! Интересно, есть ли другие способы справиться с этим. - person Jan Stolarek; 10.10.2012
comment
@killy9999: Обратите внимание, что Prelude содержит функцию asTypeOf, которая является обобщенной версией этого подхода. Так что даже если есть другие способы, этот, безусловно, классический. :] - person C. A. McCann; 10.10.2012
comment
@C.A.McCann вздох, еще один чит, как помочь выводу типа Haskell запомнить: -/ Я сомневаюсь, что это правильный способ. В итоге код становится более сложным и менее читаемым. - person David Unric; 11.10.2012

Ваш пример

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])
person shang    schedule 11.10.2012
comment
Ну, это было на самом деле больше, чтобы удовлетворить мое любопытство. Спасибо, что показали мне альтернативный подход :) - person Jan Stolarek; 11.10.2012