I am trying to create a simple scenario; where a discriminated union has records as members. Getting an error "pattern discriminator not defined" while trying to do a pattern match in a simple function.
type Circle =
{
radius : float
}
type Rectangle =
{
width : float
height : float
}
type Shape =
| ACircle of Circle
| ARectangle of Rectangle
let calcualteArea shape =
match shape with
| Circle(radius) -> System.Math.PI*radius*radius // error: pattern discriminator not defined
| Rectangle(width, height) -> width*height
Please help me resolve the error.
Thanks
The syntax is different than you're expecting in two ways. First, you should match on ACircle and ARectangle, since those are the names of the cases of your Shape type. A discriminated union case name is different from a type name. Second, the syntax for pattern matching a record looks like this:*
type Rectangle =
{ width: int
height: int }
let area rectangle =
match rectangle with
| { width = width; height = height } -> width * height
Given this, your function should look like this:
let calculateArea shape =
match shape with
| ACircle({ radius = radius }) -> System.Math.PI*radius*radius // error: pattern discriminator not defined
| ARectangle({ width = width; height = height }) -> width*height
* Note that the pattern matching here is strictly optional; you can just as easily use | ARectangle(rectangle) -> rectangle.width * rectangle.height to access the fields.
Related
I've specified a custom type which takes two floats and makes them a pair (a complex number):
type complex = (float * float);;
let makeComplex x y = complex(x,y);;
The makeComplexfunction is of type float -> float -> complex, which is entirely correct. However, I want to create a function that takes a complex type and makes it a normal pair as well.
This is the function I've tried:
let complexToPair ((x,y):complex) = (x,y);;
But the resulting type is float * float -> float * float, when it should really be complex -> float * float. Am I using the wrong syntax for the ((x,y):complex)part?
Type abbreviations do not hide or create a type distinct from their definitions. complex and float * float are still fully exchangeable, You've just given it another name.
The idiomatic way to make a distinct type in F# is to use a single-case discriminated union:
type complex = Complex of float * float
You can then write your functions using this constructor to create a value and pattern matching to deconstruct it:
let makeComplex x y = Complex (x,y)
let complexToPair (Complex (x, y)) = (x, y)
You can also hide the implementation completely from outside the module using private:
type complex = private Complex of float * float
Consumers would then have to use the functions you expose to create and consume values of the complex type.
If you still really want to use type abbreviation, then use type annotation, like this:
type complex = float * float
let complexToPair: complex -> float * float = id
You can create a class:
type Complex(x:float, y:float) =
member t.Real = x
member t.Imaginary = y
Or a record:
type Complex = { Real:float; Imaginary:float }
These will be distinct types and also allow you to create and organize useful methods (such as arithmetic operators).
I'm new to F# and I'm trying a few thing to get my head around the language.
I have to two types almost identical (Coordinate and Vector). Because of this type inference cannot work properly and I have hard time to specify the correct type on each function.
It somehow undestand that's a Vector here:
type Coordinate = {X:int; Y:int}
type Vector = {X:int; Y:int}
let calculateVector (origin:Coordinate) (destination:Coordinate) = { X=destination.X-origin.X; Y= destination.Y-origin.Y;}
And here when I want a return type of Coordinate, I cannot find how to specify the return it for this function:
let calculateNextCoordinate (coordinate:Coordinate) direction =
match direction with
| "N" -> { X=coordinate.X; Y=coordinate.Y-1 }
| "NE" -> { X=coordinate.X+1; Y=coordinate.Y-1 }
| "E" -> { X=coordinate.X+1; Y=coordinate.Y }
| "SE" -> { X=coordinate.X+1; Y=coordinate.Y+1 }
| "S" -> { X=coordinate.X; Y=coordinate.Y+1 }
| "SW" -> { X=coordinate.X-1; Y=coordinate.Y+1 }
| "W" -> { X=coordinate.X-1; Y=coordinate.Y }
| "NW" -> { X=coordinate.X-1; Y=coordinate.Y-1 }
| _ -> coordinate
I have this error on the default case: This expression was expected to have 'Vector' but here has type 'Coordinate'
i tired to have a look on this website for function signatures but could not something for my problem: https://fsharpforfunandprofit.com/posts/function-signatures/
Questions:
How do you fix this error?
Is it because inference type take by default the last type declared that match the properties (in my example Vector)?
Bonus: Is there a better way to handle this kind of situation in F#?
Thanks in advance
Since records are constructed using their member names:
{ X = 2; Y = 3}
you created a naming conflict between Vector and Coordinate. In F# the compiler always resolves to the latest definition in such cases and therefore, in your example, the compiler will interpret the record { X = ..., Y = ...} as a Vector.
There is a good article on F# records on fsharpforfunandprofit.com, that explains how to handle this easily, which I suggest you read for a good explanation.
But in short, you can prefix either member of the record instance with the record type:
{ Coordinate.X = 2; Y = 3 } // Creates a Coordinate
{ X = 2; Coordinate.Y = 3 } // Creates a Coordinate
and
{ Vector.X = 2; Y = 3 } // creates a vector
I am currently studying F# and at the same time, a bit struggling to get the hang of how discriminated unions and records exactly work.
I'd like to know how I can update some values from a list of type <'T>?
My code
type Position =
| Location of (int * int)
type Ship =
{
Position : Position;
Projectiles : List<Position>;
}
I create an instance of a ship:
let aShip =
{
Position: Location(1,5);
Projectiles: [Location(1,1);Location(2,5)]
}
Now, I tried to loop over the projectiles, but I get this:
for elem in aShip.Projectiles do
printfn "%A" elem
// prints out
Location(1,1)
Location(2,5)
But I only like to get the values (1,1) and (2,5) back, how would I achieve this?
Discriminated unions can be destructured by providing a pattern with some places in it occupied by identifiers. The compiler will then generate code that tries to match this pattern to the data, and bind data points to appropriate identifiers. For example:
let loc = Location (1,2)
let (Location (x,y)) = loc
For the second line, the compiler will generate code to the effect of "make sure this is a Location, then bind the first int to name x, and the second int to name y"
Alternatively, you can use more verbose match syntax:
let x = match loc with Location(x,y) -> x
For your specific case, this is overkill, but for discriminated unions with more than one case, match is the only way to handle them all, for example:
type Position =
| Location of int*int
| Unknown
let xOrZero =
match loc with
| Location(x,y) -> x
| Unknown -> 0
Above examples demonstrate how patterns can appear in let-bindings and within match expressions, but this is not all. In F# pretty much anything that you'd think of as "variable declaration" is actually a pattern. It's just most of the time patterns are trivial, as in let x = 5, but they don't have to be - e.g. let x,y = 5,6
The corollary from the above is that the elem in for elem in ... is also a pattern. Which means that you can destructure the element in place:
for Location(x,y) in aShip.Projectiles do
printfn "%d,%d" x y
Or, if you'd like to extract the pair as a whole, rather than x and y individually, that is also possible:
for Location xy in aShip.Projectiles do
printfn "%A" xy
I am wondering if there is a good way of exposing the content of a DU case to the DU type itself.
For example:
[<Measure>] type inch
module StructuralShape =
type Plate =
{
Length : float<inch>
Thickness : float<inch>
}
type SingleAngle =
{
VerticalLeg : float<inch>
HorizontalLeg : float<inch>
Thickness : float<inch>
}
type StructuralShape =
| Plate of Plate
| SingleAngle of SingleAngle
let area (ss : StructuralShape) =
match ss with
| Plate pl -> pl.Length * pl.Thickness
| SingleAngle sa ->
(sa.HorizontalLeg + sa.VerticalLeg - sa.Thickness) * sa.Thickness
type StructuralShape with
member ss.Area = area ss
module Test =
open StructuralShape
let myShape = Plate {Length = 2.0<inch>; Thickness = 0.25<inch>}
let area = myShape.Area
// Looking to allow the user of the library
// to access the internal shape properties
// without having to use some type of 'get shape' function
let length = myShape.Length // fails
After a re-look at my design, which was inconsistent as Fyodor pointed out, I decided to create an interface for IShape.
[<Measure>] type inch
type IShape = interface end
module StructuralShapes =
type Plate =
{
Length : float<inch>
Thickness : float<inch>
}
interface IShape
type SingleAngle =
{
VerticalLeg : float<inch>
HorizontalLeg : float<inch>
Thickness : float<inch>
}
interface IShape
let area (shape: IShape) =
match shape with
| :? Plate as pl -> pl.Length * pl.Thickness
| :? SingleAngle as sa ->
(sa.HorizontalLeg + sa.VerticalLeg - sa.Thickness)
* sa.Thickness
| _ -> failwith "Shape not suppported."
type IShape with
member this.Area = area this
Note: For simplicity, this is a simplified version of the library.
It is unclear what you want to achieve here. I suspect you may have inconsistent design in your mind and not realize it.
If your StructuralShape type only ever has one case, then why do you need it at all? Just use Plate instead!
If your StructuralShape type will have more cases, then what happens when the actual value doesn't have a Thickness field?
If your StructuralShape type will have more cases, but all of them are required to have a Thickness field, and that field has the same type and the same meaning for all of them, then a better design would be to "lift" the common fields to the common type, and leave only differences under the DU:
type Plate = { Length: float }
type Foo = { Bar: string }
type StructuralShapeInfo = Plate of Plate | Foo of Foo
type StructuralShape = { Info: StructuralShapeInfo; Thickness: float }
(pardon my choice of Info suffix; I realize it's not the best choice, but I can't come up with a better one without knowing your domain and thinking about it for a while; there are three hard problems in programming: naming stuff and off-by-one errors)
You can augment a discriminated union with members, which is useful for convenient access in cases like this:
type StructuralShape =
| Plate of Plate
member this.Thickness = match this with Plate p -> p.Thickness
If you add any new cases to StructuralShape, then you need to handle the case in the code for this property.
This may be a useful thing to know, but Fyodor makes a very good point in his answer about reorganising your data model so this isn't necessary.
I have the following code, which reports errors. I don't understand the error messages though.
module MonochromeHandler
open System.Drawing
let private transparent = Color.FromArgb(0, 0, 0, 0)
let private calculateAlpha currentAlpha greyValue =
match currentAlpha - greyValue with
| value when value < 0 -> 0
| value -> value
let private actionPixel(pixelColour:Color) =
match (pixelColour.A, pixelColour.R, pixelColour.G, pixelColour.B) with
| (0uy, _, _, _) -> transparent
| (alpha, red, green, blue) when red = blue && red = green && red <> 255uy ->
let newAlpha = calculateAlpha((int)alpha, (int)red)
Color.FromArgb(newAlpha, 0, 0, 0)
| _ -> pixelColour
I am getting an error on the "Color.FromArgb(newAlpha, 0, 0, 0)" line
Error 1 This expression was expected to have type
int
but here has type
'a -> int
The function takes two parameters, which I'm supplying and returns an int. So why isn't the type of newAlpha "int" and why is it "'a -> int"?
Possibly connected to this, since I have declared calculateAlphawithout parentheses or a comma for the parameters, my (clearly inadequate) understanding of F# is that I should be able to use it thus:
let newAlpha = calculateAlpha (int)alpha (int)red
If I do that though, I get a different error for the calculateAlpha (int)alpha part:
Error 1 This value is not a function and cannot be applied
Why can I not invoke the function in this way?
I think you meant
calculateAlpha (int alpha) (int red)
With your original parentheses, this line invokes calculateAlpha with 1 argument, and that argument is a 2-tuple:
let newAlpha = calculateAlpha((int)alpha, (int)red)
See the docs for F# Functions, specifically Partial Application of Arguments. You will see how you inadvertently created the function newAlpha!
Usually you'd write int alpha to cast an int in F#; technically, (int)alpha will work too but it's not the 'canonical' way of doing things. In any case, that's not the problem here.
The issue in your code actually arises from not surrounding the casts in parentheses; by not doing that, the F# compiler sees your code as if you'd written:
let newAlpha = (((calculateAlpha int) alpha) int) red
That's why you get the error message about expecting an int but having an 'a -> int instead -- you're passing the int function (which casts some value to an int) as the first argument to calculateAlpha.
By surrounding your casts in parenthesis, you're telling the compiler to first cast the two values, then apply the results to the calculateAlpha. This is the same thing that happens when you call the function in your original style: calculateAlpha ((int)alpha, (int)red); you could have even left off the parentheses around int there and written it like: calculateAlpha (int alpha, int red).