Рассмотрим код ниже:
t1 :: [Int] -> (Int,String)
t1 xs = (sum xs,show $ length xs)
t2 :: [Int] -> (Int,String)
t2 xs = (length xs, (\x -> '?') <$> xs)
t3 :: [Int] -> (Char,String)
t3 (x:xs) = ('Y',"1+" ++ (show $ length xs))
t3 [] = ('N',"empty")
Эти три функции имеют тип, который изменяется лишь частично — их можно полностью использовать без необходимости знать тип первого компонента создаваемого ими кортежа. Это означает, что я могу работать с ними, не обращаясь к этому типу:
fnListToStrs vs fs = (\x -> snd $ x vs) <$> fs
При загрузке этих определений в GHCi все три функции работают независимо в качестве аргумента fnListToStrs
, и действительно, я могу передать список, содержащий как t1, так и t2, потому что они имеют один и тот же тип:
*Imprec> fnListToStrs [1,2] [t1,t2]
["2","??"]
*Imprec> fnListToStrs [1,2] [t3]
["1+1"]
Но я не могу передать все 3 одновременно, хотя расхождение типов фактически не имеет отношения к выполненному расчету:
*Imprec> fnListToStrs [1,2] [t1,t2]
["2","??"]
*Imprec> fnListToStrs [1,2] [t3]
["1+1"]
У меня есть ощущение, что выполнение этой работы имеет какое-то отношение либо к экзистенциальным, либо к непредикативным типам, но ни одно из расширений не сработало для меня при использовании объявления типа, которое, как я ожидаю, может принять fnListToStrs, а именно:
fnListToStrs :: [Int] -> [forall a.[Int]->(a,String)] -> [String]
Есть ли другой способ сделать эту работу?