Reputation: 9051
In order to calculate the area of square and circle, I defined the following type:
type Square = {width: float; length: float;} with
member this.area = this.width * this.length
member this.perimeter = (this.width + this.length) * 2.
type Circle = {r:float} with
member this.area = System.Math.PI * this.r * this.r
member this.perimeter = 2. * System.Math.PI * this.r
let s1 = {width = 3.; length = 4.}
let c1 = {r = 8.3}
printfn "%A" s1
printfn "The area of s1 is: %A" s1.area
printfn "The perimeter of s1 is: %A" s1.perimeter
printfn "%A" c1
printfn "The area of c1 is: %A" c1.area
printfn "The perimeter of c1 is: %A" c1.perimeter
When I read this article: http://fsharpforfunandprofit.com/posts/type-extensions/
It states:
So, a plea for those of you new to functionally programming. Don't use methods at all if you can, especially when you are learning. They are a crutch that will stop you getting the full benefit from functional programming.
Then what's the functional way to solve this problem? or what's the idomatic F# way?
Edit:
After reading the "The F# Component Design Guidelines" (curtsy to @V.B.), and @JacquesB's comment, I consider that implement the member method within the type is the most simple, intrinsic way:
type Square2 (width: float, length: float) =
member this.area = width * length
member this.perimeter = (width + length) * 2.
(This is almost identical with my original Square
type -- this Square2
only saves seveal this.
prefix as in this.width
, this.length
.)
Again, the The F# Component Design Guidelines is quite useful.
Upvotes: 6
Views: 268
Reputation: 6382
There is a more functional way that @svick describes well, but consider also "The F# Component Design Guidelines"
This is called out specifically because some people from a functional programming background avoid the use of object oriented programming together, preferring a module containing a set of functions defining the intrinsic functions related to a type (e.g. length foo rather than foo.Length). But see also the next bullet. In general, in F#, the use of object-oriented programming is preferred as a software engineering device. This strategy also provides some tooling benefits such as Visual Studio’s “Intellisense” feature to discover the methods on a type by “dotting into” an object.
In F# there are a number of ways to represent a dictionary of operations, such as using tuples of functions or records of functions. In general, we recommend you use interface types for this purpose.
So, according to these guidelines, an interface IShape
with Area
and Perimeter
members is a recommended way for an F# component, despite there is a "more functional" way in general.
Upvotes: 1
Reputation: 244777
A more functional way to do this would be to create a Shape
discriminated union, where Square
and Circle
would be its cases. Then create functions area
and perimeter
, taking Shape
and using pattern matching:
type Shape =
| Square of Width: float * Length: float
| Circle of R: float
let area = function
| Square (width, length) -> width * length
| Circle r -> System.Math.PI * r * r
let perimeter = function
| Square (width, length) -> (width + length) * 2.
| Circle r -> 2. * System.Math.PI * r
let s1 = Square(Width = 3., Length = 4.)
let c1 = Circle(R = 8.3)
printfn "%A" s1
printfn "The area of s1 is: %A" (area s1)
printfn "The perimeter of s1 is: %A" (perimeter s1)
printfn "%A" c1
printfn "The area of c1 is: %A" (area c1)
printfn "The perimeter of c1 is: %A" (perimeter c1)
Upvotes: 8