Converting functions like `a & # 8594; b` in a function like `String & # 8594; String` in Haskell

My intention is simple. I want to wrap functions like a -> b in String -> String (so that you can add a lot of heterogeneous functions to the list). Therefore, I write:

 wrap :: (Read a, Show b) => (a -> b) -> (String -> String) wrap f = \s -> show $ f (read s :: a) 

However ghc complaints:

 Could not deduce (Read a1) arising from a use of `read' from the context (Read a, Show b) bound by the type signature for wrap :: (Read a, Show b) => (a -> b) -> String -> String 

I want to know why my piece of code does not work and what hacks are needed to achieve my goal?

Thanks.

+7
functional-programming haskell
source share
2 answers

Your code will not work because Haskell does not reuse or scope type variables; a in wrap :: (Read a, Show b) => (a -> b) -> (String -> String) is completely different from what is in read s :: a (and they are both universally quantitative) . This is source a1 in the error message; GHC is an alpha conversion program to

 wrap :: (Read a, Show b) => (a -> b) -> (String -> String) wrap f = \s -> show $ f (read s :: a1) 

However, the type of the argument f fixed inside wrap , so just deleting the type annotation works fine. Your function will become

 wrap :: (Read a, Show b) => (a -> b) -> (String -> String) wrap f = \s -> show $ f (read s) -- Or wrap f = show . f . read 

And you can use it:

 ghci> map ($ "42") [wrap (+ (7 :: Integer)), wrap (* (2.0 :: Double))] ["49","84.0"] 

Note that this means that read s has a type that you cannot write. In Haskell 2010 (or 98), the only way around this is to use a function like asTypeOf :: a -> a -> a ; asTypeOf is just const , but thanks to its type signature, it limits its first, returned argument to the same type as its second. Then, of course, you have to call such a variable of type a . To do this, the following will work:

 wrap :: (Read a, Show b) => (a -> b) -> (String -> String) wrap f = \s -> show $ f (read s `asTypeOf` fInput) where fInput = undefined fOutput = f fInput -- I still can't give this a type signature 

In GHC, to avoid this, you can enable the ScopedTypeVariables extension ; with this, if you explicitly qualify all your type variables with forall , they will be messed up just like value level names. Then your code will become

 {-# LANGUAGE ScopedTypeVariables #-} wrap :: forall a b. (Read a, Show b) => (a -> b) -> (String -> String) wrap f = \s -> show $ f (read s :: a) 

But remember that for this simple example, you don't need type annotations at all.

+13
source share

To explicitly specify read s , you'll need something like ScopedTypeVariables :

 {-# LANGUAGE ScopedTypeVariables #-} ... wrap :: forall a b. (Read a, Show b) => (a -> b) -> (String -> String) wrap f = \s -> show $ f (read s :: a) 

Since otherwise, the :: a annotation inside a function refers to another type from a in the type signature (this implicitly means :: forall a. a ). But note that you can simply completely remove type annotation:

 wrap :: (Read a, Show b) => (a -> b) -> (String -> String) wrap f = \s -> show $ f (read s) 

Since type read s can be inferred. It also simplifies the body.

 wrap f = show . f . read 
+10
source share

All Articles