I want to write ‘twice’ function that takes a function and an argument and applies the function twice. However the function that it receives should work on union types.
eg.
f a -> b
f b -> c
Output
twice f a
c
f a
b
f b
c
f c
error
eg.
f :: Int -> String
f :: String -> Char
twice f :: Int -> Cha
how do I write f that takes two types and ‘twice’ that does the transitive thing.
You’re really asking two things here: “How do I write the function
twice?”, and “how do I write anfwith two different types?”Let’s think about the first question. Letting Haskell infer types for the moment, let’s think about what it should look like. It needs to take one argument:
twice f = undefined.twicethen returns a function which takes an argument and appliesfto it twice:twice f = \x -> f (f x).But what’s the type of this function? Well,
xmust be of some typeα. Since we evaluate(f x), this means thatfmust be a function that takes in anαand returns aβ:f :: α -> β. However, we also evaluatef (f x), sofmust take aβas input as well, returning aγ:f :: β -> γ. Any single variable can only have one type, so this tells us thatα -> β = β -> γ, and soα = βandβ = γ. Thus,f :: α -> α, and so\x -> f (f x) :: α -> α; this means thattwice :: (α -> α) -> α -> α.This answers your first question. And you’ll notice that I said above that
fmust be a function from one type to the same type. This answers your second question: it is impossible to write anfwith two different types. This is because, as I said, any single variable may only have one (possibly polymorphic) type. Why? Well, among other reasons, suppose we have a variableimpossiblewith two type signatures,impossible :: Intandimpossible :: String, and two bindings,impossible = 24andimpossible = "absz". Then what doesshow impossiblereturn? Theshowfunction is of typeshow :: Show α => α -> String; since bothIntandStringare instances of theShowtypeclass, we can’t tell if this would return"42"or"\"absz\"". Inconsistencies like this are why we allow only one type.All hope is not lost, however! You also mentioned using union types to implement
f. In this context, you probably mean theEithertype (although all datatypes in Haskell are a form of union types called discriminated unions).Eitheris a type which takes two type parameters (just like[], the list type, takes one); we say that it has kind [the type of a type]Either :: * -> * -> *).Eitheris the union type:Either A Bconsists of all the elements ofAand all the elements ofB, lifted intoEither. As Michael Steele said, you can write your function with two type signatures as a function which returns anEithervalue:f :: Either δ ε -> Either δ ε. Note that this is a perfectly valid value to pass as a parameter totwice, sinceEither δ εis a perfectly legal type. We define functions onEithervia pattern matching; the two constructors ofEitherareLeft :: δ -> Either δ εandRight :: ε -> Either δ ε, for lifting the two types of values. A sample function, then, would look likeIf you really want to mimic your example and go through three types, from
αtoβtoγ, you can either use nestedEithers or define your own data type. With nestedEithers, you getWith a new type, you get:
You could also define a specific
data MyType = MyInt Int | MyStr String | MyChar Char, and replace everyEither3 Int String CharwithMyType, everyLeft3withMyInt, everyMid3withMyStr, and everyRight3withMyChar; this is effectively the same thing, but less general.Note that, thanks to Haskell’s currying, we can rewrite our original
twiceastwice f x = f (f x). And in fact, even more simply, we can write this astwice f = f (.) f, ortwice = join (.), if we importControl.Monad. This is irrelevant for the purposes of answering this question, but is interesting for other reasons (especially the(->) αinstance forMonad, which I don’t fully understand); you might want to take a look if you haven’t seen it before.