I am in new to F#, so please have mercy. Trying to model a Car Rental business in F#. My types are:
Customer
Driver
Car
VehicleType
RentalAgreement NOT IMPLEMENTED YET
My specific question is can an F# class have a member that is a Discriminated Union? A car should have an attribute that reflects what type of vehicle it is...Compact, Sedan, Truck, etc... Below is my code so far...
namespace DSL2
// a DU
type vehicleType = Truck | Compact | Econ | Sedan
// a record
type Customer = {firstName: string; lastName: string; gender: string}
//a class implicit ctor'tion
type Car(numdoors:int , make: string , year:int) = class
member this.NumDoors = numdoors
member this.Make = make
member this.Year = year
end
//a class explicit ctor'tion
type Car2 = class
val NumDoors: int
val Make: string
val Year: int
(*first ctor*)
new (numDoors, make, year) =
{NumDoors = numDoors; Make = make; Year = year}
end
Yes, a discriminated union is just like any other type and can be used as the type of any field, property, constructor parameter etc.
Just add a parameter of type vehicleType to the Car constructor:
type Car(numdoors:int, make: string, year:int, vehicleType : vehicleType) = class
member this.NumDoors = numdoors
member this.Make = make
member this.Year = year
member this.VehicleType = vehicleType
end
Note that it's bad F# style to name types with initial lower-case letters, so I'd suggest renaming it to VehicleType.
Yes, just add the member to the class and include it in the constructor with the desired value.
// a DU
type vehicleType = Truck | Compact | Econ | Sedan
// a record
type Customer = {firstName: string; lastName: string; gender: string}
//a class implicit ctor'tion
type Car(numdoors:int , make: string , year:int) = class
member this.NumDoors = numdoors
member this.Make = make
member this.Year = year
end
//a class explicit ctor'tion
type Car2 = class
val NumDoors: int
val Make: string
val Year: int
val DU: vehicleType
(*first ctor*)
new (numDoors, make, year, cust) =
{NumDoors = numDoors; Make = make; Year = year; DU = Truck }
end
Related
Is this question solvable through functional idiomatic approach, could generics or discriminated unions be the answer?
Is it possible to have polymorphism with passing different types to a function while the function is consuming some common fields.
Idea is to be able to call and reuse the function with different types and use the common attributes/fields.
type Car = {
Registration: string
Owner: string
Wheels: int
customAttribute1: string
customAttribute2: string
}
type Truck = {
Registration: string
Owner: string
Wheels: int
customField5: string
customField6: string
}
let SomeComplexMethod (v: Car) =
Console.WriteLine("Registration" + v.Registration + "Owner:" + v.Owner + "Wheels" + v.Wheels
// some complex functionality
Use
SomeComplexMethod(car)
SomeComplexMethod(truck)
Edit
Following the answer. Is it possible to specify the type of the incoming v since JSON serializer asks for the associated type. If Car was supplied as input, Car will be output, If truck as input truck will be output.
let inline someComplexFun v =
let owner = (^v: (member Owner: string)(v))
let registration = (^v: (member Registration: string)(v))
// process input
use response = request.GetResponse() :?> HttpWebResponse
use reader = new StreamReader(response.GetResponseStream())
use memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(reader.ReadToEnd()))
(new DataContractJsonSerializer(typeof<Car>)).ReadObject(memoryStream) :?> Car
if truck was the input v
(new DataContractJsonSerializer(typeof<Truck>)).ReadObject(memoryStream) :?> Truck
This looks like the classical use case for object inheritance, or perhaps an interface. The difference is that an interface provides only methods (including properties, as they are methods under the hood), while a base object (abstract or concrete) can also provide fields.
In your case an interface might be appropriate:
type IVehicle =
abstract Registration : string
abstract Owner : string
abstract Wheels : int
type Car (_r, _o, _w) =
member customAttribute1 : string
member customAttribute2 : string
interface IVehicle with
member Registration = _r
member Owner = _o
member Wheels = _w
type Truck (_r, _o, _w) =
member customField5 : string
member customField6 : string
interface IVehicle with
member Registration = _r
member Owner = _o
member Wheels = _w
let someComplexMethod (v : IVehicle) =
stdout.WriteLine "Registration: " + v.Registration +
"\nOwner: " + v.Owner +
"\nWheels: " + v.Wheels
EDIT: You can do it without OOP by using a discriminated union, and several record types:
type VehicleBase =
{ Registration : string
Owner : string
Wheels : int }
type CarAttributes =
{ customAttribute1 : string
customAttribute2 : string }
type TruckAttributes =
{ customField5 : string
customField6 : string }
type Vehicle =
| Car of VehicleBase * CarAttributes
| Truck of VehicleBase * TruckAttributes
let processVehicle v =
stdout.WriteLine ("Registration: " + v.Registration +
"\nOwner: " + v.Owner +
"\nWheels: " + v.Wheels)
let someComplexMethod = function
| Car (v, _) -> processVehicle v
| Truck (v, _) -> processVehicle v
What you want is usually called structural (or duck) typing. It can be done via interfaces and object expressions in F# (the accepted way) or via SRTPs. The link #CaringDev provided gives you a quick rundown, but obviously you can find many more examples. Please read this and this. For your specific example it will depend how much control you have over the original types.
It's easy to define another type that includes the fields that you might want. Then your function (I found it interesting btw, that you so much want to go for a functional solution but named it a method...) should just take ANY type that has the required fields/properties. Generics won't work for you in this case, as you need to constrain to a subset of types. But once you have such a function, which uses statically resolved type parameters (SRTPs) you're good to go:
type Car = {
Registration: string
Owner: string
Wheels: int
customAttribute1: string
customAttribute2: string
}
type Truck = {
Registration: string
Owner: string
Wheels: int
customField5: string
customField6: string
}
type Bike = {
Owner: string
Color: string
}
type Vehicle = {
Registration: string
Owner: string
}
let inline someComplexFun v =
let owner = (^v: (member Owner: string)(v))
let registration = (^v: (member Registration: string)(v))
{Registration = registration; Owner = owner}
let car = {Car.Registration = "xyz"; Owner = "xyz"; Wheels = 3; customAttribute1= "xyz"; customAttribute2 = "xyz"}
let truck = {Truck.Registration = "abc"; Owner = "abc"; Wheels = 12; customField5 = "abc"; customField6 = "abc"}
let bike = {Owner = "hell's angels"; Color = "black"}
someComplexFun car //val it : Vehicle = {Registration = "xyz";
//Owner = "xyz";}
someComplexFun truck //val it : Vehicle = {Registration = "abc";
//Owner = "abc";}
someComplexFun bike //error FS0001:
The Vehicle type is defined but it could be anything. Then someConplexFun is defined that can take any type, that has Owner and Registration. It has to be inline and its type signature is:
val inline someComplexFun :
v: ^v -> Vehicle
when ^v : (member get_Owner : ^v -> string) and
^v : (member get_Registration : ^v -> string)
You can pass any type that has Owner and Registration fields, and it will return a Vehicle but of course you can just print it out or return a tuple, etc. For the Bike type, since it doesn't have Registration this function will fail.
There are multiple ways how to solve this problem. Besides the already shown solution, i would use lambda functions or a new datatsructure, to solve this problem.
The idea with a lambda is. Instead of a value you expect a function as an argument that returns the needed value. As an example:
let printInformation registration owner wheels obj =
let reg = registration obj
let owner = owner obj
let wheels = wheels obj
printfn "Registration: %s Owner: %s Wheels: %d" reg owner wheels
let car = {Registration="CAR"; Owner="Me"; Wheels=4; customAttribute1="A"; customAttribute2="B"}
let truck = {Registration="TRUCK"; Owner="You"; Wheels=6; customField5="A"; customField6="B"}
printInformation
(fun (obj:Car) -> obj.Registration)
(fun obj -> obj.Owner)
(fun obj -> obj.Wheels)
car
printInformation
(fun (obj:Truck) -> obj.Registration)
(fun obj -> obj.Owner)
(fun obj -> obj.Wheels)
truck
But the whole idea is that you create such a function once for each type, and use partial application.
let printCar =
printInformation
(fun (obj:Car) -> obj.Registration)
(fun obj -> obj.Owner)
(fun obj -> obj.Wheels)
let printTruck =
printInformation
(fun (obj:Truck) -> obj.Registration)
(fun obj -> obj.Owner)
(fun obj -> obj.Wheels)
printCar car
printTruck truck
Based on this you can create a dispatch function, if you wish
let print obj =
match box obj with
| :? Car as c -> printCar c
| :? Truck as t -> printTruck t
| _ -> failwith "Not Car or Truck"
print car
print truck
print "FooBar"
Now the first ttwo works, but you lose type-safety. The third one compiles, but creates a runtime exception.
Working with lambdas is good enough if you only have 1-3 values, but if you need more values, then it can become cumbersome. Another idea would be to create your own data-type for your function, and provide conversation functions instead.
type Information = {
Registration: string
Owner: string
Wheels: int
}
let printInfo (info:Information) =
printfn "Registration: %s Owner: %s Wheels: %d" info.Registration info.Owner info.Wheels
The advantage. Now you relay on data instead of types. You can basically print any type as long you can create an Information record out of it. So its just a matter of providing a single transformation function for each type.
let carToInformation (car:Car) : Information =
{Registration=car.Registration; Owner=car.Owner; Wheels=car.Wheels}
let truckToInformation (truck:Truck) : Information =
{Registration=truck.Registration; Owner=truck.Owner; Wheels=truck.Wheels}
printInfo (carToInformation car)
printInfo (truckToInformation truck)
Sure, you can once again create a dispatch function based on this idea. Its up to you, but my personal approach i would create an Information type and use explicit conversation instead.
In my opinion it is the easiest to understand, and also the most useful one, as you can easily print any type this way as long you somehow can provide the needed data. And your different types don't need to share a common interface with pre-defined fields that have some special logic in it.
I want create an instance of Person. Person is a type of Animal. When I try to create a Person, the IDE says me "This expression was expected to have type 'Person', but here has type 'Animal'".
type Person(name) =
member this.Name: string = name
type Animal =
| Person of Person
| Cat
| Dog
let person: Person = Person(name = "John")
The problem is that Person refers to both the type and the case of the Discriminated union.
You can invert the definitions so it will resolve to the last one:
type Animal =
| Person of Person
| Cat
| Dog
and
Person (name) =
member this.Name: string = name
let person: Person = Person(name = "John")
// then to create an animal
let animal = Animal.Person (Person(name = "John"))
Alternatives solutions are to use new keyword as #MarcinJuraszek suggested in the comments or considering a different name for the DU case and the type.
In F# names acts as bindings to types and values and at any time you can redefine what a name "points" to. E.g.
// lets define type A
type A(name) = member this.Name : string = name
let x = A("test")
// lets "bind" the type name 'A' to a new type
type A(number) = member this.Number : int = number
let y = A(10)
printfn "type name of x: %s" (x.GetType().Name) // prints 'type name of x: A'
printfn "type name of y: %s" (y.GetType().Name) // prints 'type name of y: A'
So x and y are both of a type named A, but not the same. The same logic applies to Person and Animal.Person, and dependent on the order you define them in, the last defined will be the one referenced when typing Person.
As mentioned, you can use new or definition order to access both. You could also decide to put the Person class in a different module than Animal.
module Inner =
type Person(name) ...
This way you can access your types by prepending the module name.
If I've got a class hierarchy like
type Employee(name) =
member val name: string = name
type HourlyEmployee(name, rate) =
inherit Employee(name)
member val rate: int = rate
type SalariedEmployee(name, salary) =
inherit Employee(salary)
member val salary: int = salary
And I want a function that updates the name field in a pure way, how is this possible? A couple failed options:
let changeName(employee: Employee) =
// no idea what class this was, so this can only return the base class
let changeName<'a when 'a :> Employee>(employee: 'a) =
// 'a has no constructor
The closest thing I've come up with is making a virtual Employee.changeName and implementing that on each class. That just seems like a lot of extra work plus it's error-prone since the return type is Employee and has to be upcasted back to the original class.
Seems like there should be a simpler, safer way to do such a task. Is this something where typeclasses are necessary?
Update
Yes I could just make the name field mutable, which is how it is implemented in my code now, but that's what I'm wanting to get away from.
Update 2
A solution I've come up with, that meets type safety and conciseness requirements, would be to define
type Employee<'a> = {name: string; otherStuff: 'a}
and then just use with syntax to change the name. But otherStuff: 'a is obviously ugly and hacky looking code, so I'm still open to better solutions.
If you're looking for something both pure and idiomatic F#, then you shouldn't be using an inheritance hierarchy in the first place. That's an object-oriented concept.
In F#, you could model Employee like this, using algebraic data types:
type HourlyData = { Name : string; Rate : int }
type SalaryData = { Name : string; Salary : int }
type Employee =
| Hourly of HourlyData
| Salaried of SalaryData
This would enable you to create Employee values like this:
> let he = Hourly { Name = "Bob"; Rate = 100 };;
val he : Employee = Hourly {Name = "Bob";
Rate = 100;}
> let se = Salaried { Name = "Jane"; Salary = 10000 };;
val se : Employee = Salaried {Name = "Jane";
Salary = 10000;}
You can also define a function to change the name in a pure manner:
let changeName newName = function
| Hourly h -> Hourly { h with Name = newName }
| Salaried s -> Salaried { s with Name = newName }
This enables you to change the name of an existing Employee value like this:
> let se' = se |> changeName "Mary";;
val se' : Employee = Salaried {Name = "Mary";
Salary = 10000;}
I am running into an issue with combining attributes when using ServiceStack.Redis with f#. Maybe I am thinking about this wrong but right now I'd like my type to be seralized to JSON but also passable to ServicvStack. The issue with f# types is that there is no default constructor and this the data added to my Redis instance is emtpy, well the record is there but none of the data inside it is there.
Here is an example:
open System
open ServiceStack.Redis
[<CLIMutable>]
[<DataContract>]
type Car = {
[<field: DataMember(Name="ID")>]
ID : int64
[<field: DataMember(Name="Make")>]
Make : string
[<field: DataMember(Name="Model")>]
Model : string
}
let redisCar = redis.As<Car>()
let redisAddCar car : Car =
let redisCar = redis.As<Car>()
redisCar.Store({ car with ID = redisCar.GetNextSequence() })
let car1 = redisAddCar { ID = 0L; Make = "Honda"; Model = "Accord LX" }
let car2 = redisAddCar { ID = 0L; Make = "Honda"; Model = "Accord EX" }
let car3 = redisAddCar { ID = 0L; Make = "Honda"; Model = "Accord SX" }
let cars = redisCar.GetAll()
cars |> Seq.iter (fun car -> printfn "ID: %i, Make: %s, Model: %s" car.ID car.Make car.Model)
This will print out:
ID: 0, Make: , Model:
ID: 0, Make: , Model:
ID: 0, Make: , Model:
However if I change the type to this:
[<CLIMutable>]
[<DataContract>]
type Car = {
[<field: DataMember(Name="ID")>]
mutable ID : int64
[<field: DataMember(Name="Make")>]
mutable Make : string
[<field: DataMember(Name="Model")>]
mutable Model : string
}
It will then print out:
ID: 1, Make: Honda, Model: Accord LX
ID: 2, Make: Honda, Model: Accord EX
ID: 3, Make: Honda, Model: Accord SX
Why do I have to add mutable to each property even if it was defined through the attribute? I'd prefer it to not be mutable, as I don't want the state to change. Maybe I am thinking about this too much and should just make a new type and translate the immutable type to the mutable type so it can be consumed with ServiceStack.Redis?
CLIMutable attribute doesn't affect record behavior when record is used from F# code. For F# code it is still immutable record. See here: http://blogs.msdn.com/b/fsharpteam/archive/2012/07/19/more-about-fsharp-3.0-language-features.aspx
"In F# 3.0, we’ve added CLIMutableAttribute. If you attach this
attribute to an F# record type, then the F# compiler emits a default
constructor and property setters into the generated IL for this type
(though those features are not exposed to F# code)."
type ProcessParametersPair = {Definition:string; Provided:string}
type QueryResult = { DefinitionId:int;DefinitionName:string; ProcessTemplateId:int; StatusName:string; DefinitionArgs:string; BuildArgs:string;
StatusCode:int option;cleanWorkspaceOption:string; RawProcessParameters:ProcessParametersPair; lastBuild:Tbl_Build}
type QueryDisplay = { DefinitionId:int;DefinitionName:string; ProcessTemplateId:int; StatusName:Object; DefinitionArgs:string; BuildArgs:string;
StatusCode:int option;cleanWorkspaceOption:string; RawProcessParameters:Object; lastBuild:Object}
do I really have to repeat all fields of the QueryDisplayRecord that match? Can I do something like you can do with record instances? type QueryDisplay = {QueryResult with lastBuild:Object}
In this case I'm varying record based on a field type, I'd also love to know if I can do this but on added fields instead of type changed fields.
I am not referring to instances, I'm referring to Record type definitions.
Are either of these possible?
A record type cannot "inherit" from another type. You could put all the common fields in a separate type and reference it:
type Common = { A: int; B: int }
type Record1 = { Common: Common; C: int }
type Record2 = { Common: Common; D: int }
Or use classes:
type Common(a, b) =
member val A = a
member val B = b
type Class1(a, b, c) =
inherit Common(a, b)
member val C = c
type Class2(a, b, d) =
inherit Common(a, b)
member val D = d