Question

I'm trying to write a function which allows you to "apply" a list of Strings to an arbitrary function. Here's what I've got so far:

{-# 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"

Examples:

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"

I'd like to write something like the following:

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

but GHC complains with: Could not deduce (Show a0) arising from a use of 'show' ...

However, specific definitions work:

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

and result in functions of type [String] -> String:

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

Is there a way I can get wrap to work? Is there a better way to implement apply?

No correct solution

OTHER TIPS

You can also try changing the declaration of Apply to

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

And adding the FunctionalDependencies extension, your current wrap signature works

I believe this is all related to the Overlapping Instances error that you get after the "Could not deduce" error.

It seems to work if you add ScopedTypeVariables and write this:

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

To be honest I'm not quite sure why this fixes it, but you're in delicate territory with the two overlapping instances for Apply and I think this was needed to persuade GHC to use the passed in Apply instance declared in the type signature of wrap instead of resolving the call to apply immediately with the a ~ b instance and then wanting Show for the type of f as a consequence.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top