开发者

How do I write the function 'twice' in Haskell?

开发者 https://www.devze.com 2022-12-23 20:03 出处:网络
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.

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开发者_如何学JAVA
   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 an f with 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. twice then returns a function which takes an argument and applies f to it twice: twice f = \x -> f (f x).

But what's the type of this function? Well, x must be of some type α. Since we evaluate (f x), this means that f must be a function that takes in an α and returns a β: f :: α -> β. However, we also evaluate f (f x), so f must 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 that twice :: (α -> α) -> α -> α.

This answers your first question. And you'll notice that I said above that f must be a function from one type to the same type. This answers your second question: it is impossible to write an f with 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 variable impossible with two type signatures, impossible :: Int and impossible :: String, and two bindings, impossible = 24 and impossible = "absz". Then what does show impossible return? The show function is of type show :: Show α => α -> String; since both Int and String are instances of the Show typeclass, 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 the Either type (although all datatypes in Haskell are a form of union types called discriminated unions). Either is 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 :: * -> * -> *). Either is the union type: Either A B consists of all the elements of A and all the elements of B, lifted into Either. As Michael Steele said, you can write your function with two type signatures as a function which returns an Either value: f :: Either δ ε -> Either δ ε. Note that this is a perfectly valid value to pass as a parameter to twice, since Either δ ε is a perfectly legal type. We define functions on Either via pattern matching; the two constructors of Either are Left :: δ -> Either δ ε and Right :: ε -> Either δ ε, for lifting the two types of values. A sample function, then, would look like

f :: Either Int String -> Either Int String
f (Left  n) = Right $ "The number " ++ show n
f (Right s) = Left  $ length s

-- f (Left 3)               == Right "The number 3"
-- f (Right "The number 3") == Left 12
-- twice f (Left 3)         == Left 12

If you really want to mimic your example and go through three types, from α to β to γ, you can either use nested Eithers or define your own data type. With nested Eithers, you get

f :: Either Int (Either String Char) -> Either Int (Either String Char)
f (Left  n)         = Right $ Left  $ "The number " ++ show n
f (Right (Left  s)) = Right $ Right $ head $ drop 11 s
f (Right (Right c)) = Left  $ fromEnum c

-- f (Left 42)                      == Right (Left "The number 42")
-- f (Right (Left "The number 42")) == Right (Right '4')
-- f (Right (Right '4'))            == Left 52
-- twice f (Left 42)                == Right (Right '4')

With a new type, you get:

data Either3 a b c = Left3 a | Mid3 b | Right3 c deriving (Eq, Ord, Read, Show)

f :: Either3 Int String Char -> Either3 Int String Char
f (Left3  n) = Mid3   $ "The number " ++ show n
f (Mid3   s) = Right3 $ head $ drop 11 s
f (Right3 c) = Left3  $ fromEnum c

-- f (Left3 42)             == Mid3 "The number 42"
-- f (Mid3 "The number 42") == Right3 '4'
-- f (Right3 '4')           == Left3 52
-- twice f (Left3 42)       == Right3 '4'

You could also define a specific data MyType = MyInt Int | MyStr String | MyChar Char, and replace every Either3 Int String Char with MyType, every Left3 with MyInt, every Mid3 with MyStr, and every Right3 with MyChar; this is effectively the same thing, but less general.

Note that, thanks to Haskell's currying, we can rewrite our original twice as twice f x = f (f x). And in fact, even more simply, we can write this as twice f = f (.) f, or twice = join (.), if we import Control.Monad. This is irrelevant for the purposes of answering this question, but is interesting for other reasons (especially the (->) α instance for Monad, which I don't fully understand); you might want to take a look if you haven't seen it before.


Your transitive thing is commonly called function composition which is available through the . operator.

f . g = \x -> f(g x)

Twice instead is an example of self-composition (function iteration), which you can express through f . f.

But note that there are no overloaded functions in Haskell - Every function in one scope has exactly one type and implementation (though this type may be polymorphic).


Such a twice function would look like this:

twice :: (a -> a) -> a -> a
twice f = f . f

Let's say you had a function called sayIt that converts Int values to English.

sayIt :: Int -> String
sayIt 1 = "One"
sayIt _ = "I don't know!"

There's no way to make the twice function work with sayIt:

*Main> sayIt (sayIt 1)

<interactive>:1:7:
    Couldn't match expected type `Int' against inferred type `String'
    In the first argument of `sayIt', namely `(sayIt 1)'
    In the expression: sayIt (sayIt 1)
    In the definition of `it': it = sayIt (sayIt 1)
*Main> twice sayIt 1

<interactive>:1:6:
    Couldn't match expected type `Int' against inferred type `String'
    In the first argument of `twice', namely `sayIt'
    In the expression: twice sayIt 1
    In the definition of `it': it = twice sayIt 1

sayIt only takes Int values, so calling a second time with a String value is an error.

union types

You can only use twice for functions that take and return the same type. Since you asked about "union" types, here's an example of such a function:

sayIt2 :: Either Int String -> Either Int String
sayIt2 (Left 1)    = Right "One"
sayIt2 (Right str) = Right str
sayIt2 _           = Right "I don't know!"

example:

*Main> twice sayIt2 (Left 1)
Right "One"
*Main> twice sayIt2 (Left 2)
Right "I don't know!"
*Main> twice sayIt2 (Right "Hello")
Right "Hello"


twice f = f . f
0

精彩评论

暂无评论...
验证码 换一张
取 消