применение списка строк к произвольной функции

Я пытаюсь написать функцию, которая позволяет вам «применить» список строк к произвольной функции. Вот что у меня есть:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, OverlappingInstances, TypeFamilies #-}

class Apply a c
  where apply :: a -> [String] -> c

instance (Read a, Apply b c) => Apply (a -> b) c
  where apply _ [] = error "not enough arguments"
        apply f (x:xs) = apply (f (read x)) xs

instance (a ~ b) => Apply a b 
  where apply f [] = f
        apply _ (_:_) = error "too many arguments"

Примеры:

g1 :: Int -> Bool
g1 x = x > 10

g2 :: Bool -> Int -> Int
g2 b n = if b then 10*n else n-1

test1 = apply g1 ["3"]              -- False
test2 = apply g2 ["True", "33"]     -- 330
test3 = apply g2 ["False", "0"]     -- -1
test4 = apply g2 []                 -- error "not enough arguments"
test5 = apply g2 ["True", "3", "x"] -- error "too many arguments"
test6 = apply (length :: [Int] -> Int) ["[4,5,6]"]           -- 3
test7 = apply (tail :: [Char] -> [Char]) [ "['a','b','c']" ] -- "bc"

Я хотел бы написать что-то вроде следующего:

wrap :: (Show b, Apply a b) => a -> ([String] -> String)
wrap f xs = show $ apply f xs

но GHC жалуется на: Could not deduce (Show a0) arising from a use of 'show' ...

Тем не менее, конкретные определения работают:

w1 xs = show $ apply g1 xs
w2 xs = show $ apply g2 xs

и результат в функциях типа [String] -> String:

test8 = w1 ["20"]          -- "True"
test9 = w2 ["False", "3" ] -- "2"

Есть ли способ заставить wrap работать? Есть ли лучший способ реализовать apply?


person ErikR    schedule 26.01.2014    source источник


Ответы (2)


Вы также можете попробовать изменить объявление Apply to

class Apply a c | a -> c
     where apply :: a -> [String] -> c

И добавив расширение FunctionalDependencies, ваша текущая подпись обертки работает

Я считаю, что все это связано с ошибкой Overlapping Instances, которую вы получаете после ошибки «Could not deduce».

person Charles Durham    schedule 26.01.2014
comment
В любом случае это кажется хорошей идеей, поскольку устраняет двусмысленность в отношении того, что является результатом каррированной функции. - person GS - Apologise to Monica; 26.01.2014
comment
Вам также пришлось добавить LANGUAGE UndecidableInstances? - person ErikR; 26.01.2014

Кажется, это работает, если вы добавите ScopedTypeVariables и напишите это:

wrap :: forall a b . (Show b, Apply a b) => a -> ([String] -> String)
wrap f xs = show (apply f xs :: b)

Честно говоря, я не совсем уверен, почему это исправляет это, но вы находитесь на деликатной территории с двумя перекрывающимися экземплярами для Apply, и я думаю, что это было необходимо, чтобы убедить GHC использовать переданный экземпляр Apply, объявленный в сигнатуре типа wrap вместо того, чтобы немедленно разрешить вызов apply с экземпляром a ~ b и, как следствие, желать Show для типа f.

person GS - Apologise to Monica    schedule 26.01.2014