Reputation: 36915
I was following some examples on F# Wikibook on High Order Functions.
Second code snippet under title, Composition Function has following code snippet.
#light
open System
let compose f g x = f (g x)
let xSquared x = x*x
let negXPlusFive x = -x/2.0 + 5.0
let fog = compose xSquared negXPlusFive
// ... Console.WriteLine statements....
The one I am having problem understanding is
let xSquared x = x*x
When I run it with F# interactive shell (fsi.exe) by itself, I get the following signature.
> let xSquared x = x*x;;
val xSquared : int -> int
But when I run the whole code snippet, xSquared
returns the following.
val compose : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b
val xSquared : float -> float
val negXPlusFive : float -> float
val fog : (float -> float)
Why does xSquared
takes float
and returns float
?
Upvotes: 1
Views: 532
Reputation: 10006
To expand on what Sebastian said and what jleedev tagged, a function like:
let xSquared x = x*x
Can only work on a type that has an operator *. By default, int wins in these situations. It cannot be truly generic because .NET doesn't have a way of representing the constraint "any type that has *".
However, F# supports inlining, which allows functions to be more generic, because they are inlined into each callsite. This allows you to have a function like xSquared that works on floats, ints, etc., -- any type with a * operator.
> let inline xSquared x = x*x;;
val inline xSquared :
^a -> ^b when ^a : (static member ( * ) : ^a * ^a -> ^b)
Now notice how the function type is ^a -> ^b. This is similar to 'a -> 'b, except the type variables must be statically resolved. Because F# doesn't have typeclasses, this is how operators are handled.
You can actually define your own type with it's own * member to do whatever you desire, and it'd work with xSquared:
type Foo(x) =
member this.Y = x - 1
static member (*) (x:Foo, y:Foo) = string <| x.Y * y.Y + 1
let a = Foo(10);;
type Foo =
class
new : x:int -> Foo
member Y : int
static member ( * ) : x:Foo * y:Foo -> string
end
val a : Foo
> xSquared a;;
val it : string = "82"
Just open prim-types.fs in your F# distro and poke around. Around line 2200 are the definitions for >>> and others which show off inlining and other nifty things.
Upvotes: 11
Reputation: 6360
With more information, F# can determine that xSquared is called with float arguments. If you change negXPlusFive to something like "let negXPlusFive x = -x + 5" you would find that it, fog and xSquared would be "int -> int".
Upvotes: 3