Newbie f# question - f#

I have a simple function call takes two tuples. Getting compiler error on type:
module test
open System.IO
open System
let side (x1,y1) (x2,y2) : float =
Math.Sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1))
let a = side ( 2s, 3s ) ( 1s, 2s )
Error 2 The type 'float' does not match the type 'int16'
Not sure where it goes wrong. Can anyone help?
Thanks!

Math.Sqrt expects argument of float, but you pass there int16. F# doesn't perform such implicit conversions
let side (x1,y1) (x2,y2) : float =
(x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)
|> float
|> Math.Sqrt
or you can pass floats from the very beginning:
let side (x1,y1) (x2,y2) : float = Math.Sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1))
let a = side ( 2.0, 3.0 ) ( 1.0, 2.0 )

As others already pointed out, the F# compiler doesn't automatically insert any conversions between numeric types. This means that if you're writing a function that works with floats, you need to pass it floats as arguments.
The function in your example can work with various types, because Math.Sqrt and numeric operators are overloaded. If you write it without any type annotations, you'll get a function working with floats (because Math.Sqrt only works with floats):
> let side (x1,y1) (x2,y2) =
Math.Sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));;
val side : float * float -> float * float -> float
This can be called only with floats as arguments, so you need to call it like Joel suggests. If you want to get a function that takes other type of number as parameter, you'll need to add type annotations and conversion. I would write it like this:
> let side (x1:int16,y1) (x2,y2) =
let n = (x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)
Math.Sqrt(float(n));;
val side : int16 * int16 -> int16 * int16 -> float
We need only a single type annotation (the compiler then figures out that y1, x2, ... also have to be of type int16, because we're multiplying/adding them and that's only allowed on two values of the same type). So, now you can write:
side ( 2s, 3s ) ( 1s, 2s )
Note that the version by desco is a bit tricky - it adds conversion (using the float) function, but it doesn't have type annotation to specify the type of parameters - in this case, the compiler will pick a default type which is int, so if you use his function, you'll have to call it using side (2,3) (1,2).

The signature of your function is float * float -> float * float -> float but you're passing in int16 values (that's what the s suffix means).
One way to get it to compile would be to do this:
let a = side ( 2.0, 3.0 ) ( 1.0, 2.0 )

Related

F# FS0001: This Expression was expected to have type figure, but her has type int*int

I am currently working on a program that is supposed to take a 'figure' and move it along a vector. For this I have created the function 'move' which takes a 'figure' and a vector.
I am then trying to use pattern-matching to update the values of the figure.
type point = (int*int)
type figure =
|Circle of point * int * string
|Rectangle of point * point * string
|Mix of figure * figure
let circ = Circle((50,50),45,"Red")
let rect = Rectangle((40,40),(90,110),"Blue")
let figTest : figure = Mix(circ,rect)
I have the above types and the starting figure, 'figTest'.
I then call 'move figTest', but it gives the error FS0001: This expression was expected to have type 'figure' but here has type 'int * int -> figure'.
let rec move figure (v:int*int) : figure=
let vx = fst v
let vy = snd v
match figure with
| Circle ((cx,cy) , radius, colour) -> Circle(point(cx + vx, cy-vy), radius, colour)
| Rectangle((x0,y0), (x1,y1), colour) -> Rectangle(point(x0 + vx, y0 + vy),point(x1 + vx, y1 + vy), colour)
| Mix(f1,f2) ->
let newCirc = move(f1)
let newRect = move(f2)
let newFig = Mix(newCirc, newRect)
newFig
The error seems to occur when i give 'newFig' the new circle and rectangle, but I can't quite figure out what is wrong. I struggle quite a bit with these type errors in f# pretty often, so I thought I was getting the hang of it, but I just can't find the cause of this...
This is very close to working, so don't get discouraged. There are two minor problems:
When you call move recursively, you have to pass v again. So let newCirc = move f1 v is correct instead of let newCirc = move(f1). (Note that newCirc might not actually be a circle, so you might want to use a different variable name.)
Since point is just a synonym for int * int, it doesn't have its own constructor function. So (cx + vx, cy-vy) is correct instead of point(cx + vx, cy-vy).
When I made these two changes, your code worked correctly for me. There are a number of other issues with your code that you might want to address, but those are the only two that are show-stoppers at this point.

Bit Shifting with units of measure in F#

I have three types of position - called Position, WalkPosition and TilePosition. I've converted them to units of measure, and it is much cleaner, but some things just don't quite work for me.
Unfortunately, I'm not using purely F# (there's a C++ interface exposed via CLI - fun times!). Firstly, for converting in and out, I've used * 1<tile> and * 1<1/tile> because I noticed a performance hit on my use of int. That's all well and good until I start trying to do interesting things with generics. I'm currently stumped by the getApproxDistance function, with an operator I'm calling |~|. This version assumes no unit of measure attached to my Position:
[<Measure>] type pixel
[<Measure>] type walk
[<Measure>] type tile
module Position =
type Position<[<Measure>] 'u> = Pos of int<'u> * int<'u> with
static member inline (+) (Pos (x1, y1), Pos (x2, y2)) = Pos (x1 + x2, y1 + y2)
static member inline (-) (Pos (x1, y1), Pos (x2, y2)) = Pos (x1 - x2, y1 - y2)
static member inline (*) (Pos (x, y), f) = Pos (x * f, y * f)
static member inline (/) (Pos (x, y), d) = Pos (x / d, y / d)
// getApproxDistance as per Starcraft: Broodwar
static member (|~|) (Pos (x1, y1), Pos (x2, y2)) =
let xDist = abs (x1 - x2)
let yDist = abs (y1 - y2)
let largeDist, smallDist = max xDist yDist, min xDist yDist
if smallDist < (largeDist >>> 2) then largeDist
else
let smallCalc = (3*smallDist) >>> 3
((smallCalc >>> 5) + smallCalc + largeDist - (largeDist >>> 4) - (largeDist >>> 6))
// Precise length calc - may be slow
static member inline (|-|) (Pos (x1, y1), Pos (x2, y2)) =
pown (x1 - x2) 2 + pown (y1 - y2) 2 |> float |> sqrt
let inline posx (Pos (_x, _)) = _x
let inline posy (Pos (_, _y)) = _y
let PixelPerWalk : int<pixel/walk> = 8<pixel/walk>
let PixelPerTile : int<pixel/tile> = 32<pixel/tile>
let WalkPerTile : int<walk/tile> = 4<walk/tile>
let inline walkToPixel (pos:Position<_>) = pos * PixelPerWalk
let inline tileToPixel (pos:Position<_>) = pos * PixelPerTile
let inline pixelToWalk (pos:Position<_>) = pos / PixelPerWalk
let inline tileToWalk (pos:Position<_>) = pos * WalkPerTile
let inline pixelToTile (pos:Position<_>) = pos / PixelPerTile
let inline walkToTile (pos:Position<_>) = pos / WalkPerTile
let example = Pos (1<walk>, 2<walk>) |~| Pos (2<walk>, 1<walk>)
I'd be content to rip the unit of measure off (|> int doesn't seem to slow it down in this scenario) and add it back on the returned distance, but it seems I can't do that. I can't even overload an inline call, because you can't overload purely on units of measure. Ideas?
The error that you are seeing is because the units of measure in your |~| function are being constrained to 1. This means that an int<walk> is not a valid input to this function.
You are correct that the int function will strip units of measure from something of type int<'u>. What you may not be aware of is that you can use LanguagePrimitives.Int32WithMeasure<'u> to add a particular unit of measure.
You can therefore write |~|:
static member (|~|) (Pos (x1 : int<'u>, y1 : int<'u>), Pos (x2 : int<'u>, y2 : int<'u>)) =
let xDist = abs (int x1 - int x2)
let yDist = abs (int y1 - int y2)
let largeDist, smallDist = max xDist yDist, min xDist yDist
if smallDist < (largeDist >>> 2) then
largeDist
|> LanguagePrimitives.Int32WithMeasure<'u>
else
let smallCalc = (3*smallDist) >>> 3
((smallCalc >>> 5) + smallCalc + largeDist - (largeDist >>> 4) - (largeDist >>> 6))
|> LanguagePrimitives.Int32WithMeasure<'u>
Your example:
let example = Pos (1<walk>, 2<walk>) |~| Pos (2<walk>, 1<walk>)
This then has the correct type: int<walk>.
You shouldn't need to worry about the performance impact of stripping away/adding units of measure, they are just a compile time construct - unit of measure information is not retained at runtime.
By the way, you also don't really need all of those inline keywords everywhere, you aren't doing anything with statically resolved type parameters, see https://msdn.microsoft.com/en-us/library/dd548047.aspx for more detail about use of inline.

Method overload with unit of measure

I'm noobing around with F# trying to create overloads of cos that accepts angles with units.
This is my code:
[<Measure>] type rad
[<Measure>] type deg
let toRad(x:float<deg>) =
(float x) * 3.14159265 / 180.0
|> LanguagePrimitives.FloatWithMeasure<rad>
let cos (angle: float<rad>) = cos(float angle)
let cos (angle: float<deg>) = cos(toRad angle) // get duplicate definition of cos here
Compiler complains about duplicate definition of cos on the last row.
Measure types are erased (see the specification), so you effectively have two definitions of cos(angle: float) which causes the error.
You could create a union type for the two possibilities
type Angle = Degrees of float | Radians of float
or give the functions different names.

F# Quadruple Double Function

According to the TryF#.org site this function below returns quadruple of the number entered.
let quadruple x =
let double x = x * 2
double(double(x))
Can anyone explain why as I interpret it as like follows? Quadruple doesn't perform any mutation or multiple calls.
function quadruple(x)
return function double(x)
return x * 2
or C#
int a(int x) { return b(x); }
int b(int x) { return x * 2; }
I think this is just a confused indentation. The function should probably look like this:
let quadruple x =
let double x = x * 2
double(double(x))
This should hopefully make more sense - the quadruple function defines a function double and then calls it on the input x (multiplying it by 2) and then applies double on the result, multiplying it by 2 again, so the result is (x * 2) * 2.
Using the indentation in your sample, the code would not compile, because it is not syntactically valid (a function body cannot end with a let line - it needs to end with an expression representing some result to be returned).

F# type extension with units of measure type conversion resulting in strange error

I have a function that converts from my own implementation of a 3D vector (which supports units of measure) to XNA's implementation:
type Vector3<[<Measure>]'a> with
member inline v.ToXna() =
Microsoft.Xna.Framework.Vector3(v.x / 1.f<_>, v.y / 1.f<_>, v.z / 1.f<_>)
When I compile it, I get a strange error:
The signature and implementation are not compatible because the type
parameter in the class/signature has a different compile-time
requirement to the one in the member/implementation
The inline seems to be a necessity; without it, I get this error:
This construct causes code to be less generic than indicated by the
type annotations. The unit-of-measure variable 'a has been constrained
to be measure 'm'.
Any idea what's going on?
Edit To answer #svick's questions, Vector3 is defined as:
type Vector3<[<Measure>]'u> =
struct
val x:float32<'u>
val y:float32<'u>
val z:float32<'u>
new(x, y, z) = { x = x; y = y; z = z }
end
And I'm also having some type inference problems defining it as a normal function:
let vector3 (v:DumpEngine.Vector3<_>) =
Vector3(v.x / 1.f<_>, v.y / 1.f<_>, v.z / 1.f<_>)
Causes the function to be a Vector3<1> -> Microsoft.Xna.Framework.Vector3, which makes it quite unusable. I'm not sure this is a related issue, though.
Another solution, which circumvents the problem entirely by using a cast rather than a divison:
type Vector3<[<Measure>]'u> with
member inline v.ToXna() =
Microsoft.Xna.Framework.Vector3(float32 v.x, float32 v.y, float32 v.z)
I have no idea what's going on, but this seems to work:
let inline ToXna(v:Vector3<'a>) =
Microsoft.Xna.Framework.Vector3(v.x / 1.f<_>, v.y / 1.f<_>, v.z / 1.f<_>)
It's the best I managed to do, though.

Resources