Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Polymorphic function to convert between two types

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...

like image 662
dumb0 Avatar asked Feb 19 '26 14:02

dumb0


2 Answers

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
like image 153
duplode Avatar answered Feb 21 '26 15:02

duplode


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.

like image 33
Vladimir Matveev Avatar answered Feb 21 '26 15:02

Vladimir Matveev