applying a list of Strings to an arbitrary function

北慕城南 提交于 2019-12-11 02:56:06

问题


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?


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/21362498/applying-a-list-of-strings-to-an-arbitrary-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!