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.
Related
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.
I am trying to build a degrees/radians calculator. Here are the functions I have.
func degreesToRadians(degrees: Double) -> Double {
return degrees * (M_PI / 180)
}
func radiansToDegrees(radians: Double) -> Double {
return radians * (180 / M_PI)
}
degreesToRadians(90)
radiansToDegrees(M_PI/4)
degreesToRadians(90) returns 1.5707963267949
radiansToDegrees(M_PI/4) returns 45.0
What I want to happen is in the Degrees to Radians function instead of 1.5707963267949 I want the output to be π/2. Is this even possible?
Thanks.
To represent 90 degrees as π/2, what you want to do is
consider the fraction of degrees over 180 (i.e. numerator of degrees and denominator of 180);
reduce this fraction (i.e. divide both the numerator and denominator by the greatest common factor); in the case of 90/180, the greatest common factor is, in fact, 90, yielding a reduced numerator of 1 and a reduced denominator of 2;
the result as a string representation of the fraction, i.e. something like
"\(reducedNumerator)π/\(reducedDenominator)"
Obviously, if either the numerator or denominator are 1, then you can suppress that portion of the string, but hopefully you get the basic idea.
Take a crack at that.
If you want exactly: "π/2", I don't think this is possible with double. You might get this in String, but not in a number. I think your best option would be to take an optional bool in which case result is returned as multiple of pi.
degreesToRadians(90, resultAsMultipleOfPi:true)
If this bool is true then 0.5 should be returned. You may need to do some rounding off to get well rounded numbers.
If you look closely on what is really M_PI you will see that it is predefined double value in math.h that equals
#define M_PI 3.14159265358979323846264338327950288 /* pi */
If you want more precision you can declare and use long double value instead.
You can directly use M_PI constant.
If you need in float format just use this,
let pi = Float(M_PI)
Suppose I have a tuple of numbers:
let mynum = (3, 5, 8.9, 45, 127.3)
It's mixed int with float. In order to do calculations like average, I have to convert them to float. How to do the conversion?
I don't know how did you end up with that tuple, I would advise to review your design. I personally consider tuples of more than 4 elements a smell, may be a record with named elements would be a best fit.
Anyway you can convert it easily to a list of float and then calculate the average:
let mynum = (3, 5, 8.9, 45, 127.3)
let inline cnv (x1, x2, x3, x4, x5) = [float x1; float x2; float x3; float x4; float x5]
let lst = cnv mynum // float list = [3.0; 5.0; 8.9; 45.0; 127.3]
List.average lst // float = 37.84
How did the input turn out in this format? Tuples aren't supposed to be used in this way; they are intended for combinations of a few objects, which are strongly and independently typed.
The type of a tuple changes with its length, so there is no straightforward way to perform sequence operations on them. They are not compatible with seq<'T> anyway, since they the types of their components are unrelated. There is no average function for tuples, and if there were, it would have to be overloaded for all possible arities (number of components).
You might want to import the data into another collection, such as a list, a set, an array, or another type of sequence, and have the importer handle conversions where necessary. For example, if the input were a list of strings (as taken from a text file or such), System.Double.Parse or, as pointed out by ildjarn in the comments, the float operator, can be used to turn them into floats:
let input = ["3"; "5"; "8.9"; "45"; "127.3"]
List.map float input
This returns [3.0; 5.0; 8.9; 45.0; 127.3], which is of type float list: an immutable, singly-linked list of double-precision floats.
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.
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 )