Is it possible to code in either Haskell98 or with extensions the following function f?
f :: (a -> b) -> (b -> a) -> c -> d
where (c,d) :: (a,b) || (c,d) :: (b,a)
I am attempting to create some generic function to convert between any two types a and b but am having difficulty with both being somewhat arbitrary. They are immediately set however by the first argument a ->b, so I'm hoping this is possible. Any guidance is appreciated...
Thanks!
[EDIT]
The way I see it, my question question boils down to whether haskell's type system supports the or keyword... as the intuitive signature of my function is: f :: (a->b) -> (b->a) -> a -> b or (a->b) -> (b->a) -> b -> a...
As alecb explains, it can't be done in the way your signature suggests, at least through ordinary means (it would require checking the types at runtime). An alternative you might like are Iso and Prism from lens. Iso fits the bill if your conversion functions are total and bijective; if one of the functions is partial you can use a Prism instead. Here is a throwaway example:
import Control.Lens
import Text.ReadMaybe
stringInt :: Prism' String Int
stringInt = prism' show readMaybe
GHCi> "3" ^? stringInt
Just 3
it :: Maybe Int
GHCi> 3 ^. re stringInt
"3"
it :: String
I doubt that it is possible to do what you want directly, but you can use some workarounds:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
class Converter a b c where
($$) :: a -> b -> c
data Conv a b = Conv a b
instance Converter (Conv (a -> b) (b -> a)) a b where
($$) (Conv f _) = f
instance Converter (Conv (a -> b) (b -> a)) b a where
($$) (Conv _ g) = g
intToString :: Int -> String
intToString = show
stringToInt :: String -> Int
stringToInt = read
printString :: String -> IO ()
printString = print
printInt :: Int -> IO ()
printInt = print
main = do
let convert = Conv intToString stringToInt
printString $ convert $$ (12345 :: Int)
printInt $ convert $$ "12345"
I believe this is sufficiently close to what you want. The only difference is a mandatory $$ operator, but it is inevitable.
Update: you can even drop special Conv structure and use plain tuples:
instance Converter (a -> b, b -> a) a b where
($$) (f, _) = f
instance Converter (a -> b, b -> a) b a where
($$) (_, g) = g
-- ...
printString $ (intToString, stringToInt) $$ (12345 :: Int)
printInt $ (intToString, stringToInt) $$ "12345"
I think this is even closer to your requirement.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With