4 people like it.

F# version of Type Classes from OO Perspective

This is the F# translation of the Scala program written for https://functional.works-hub.com/blog/Type-classes-from-OO-perspective. F# doesn't have implicits, so type classes don't work. However, it's interesting to see just how close you can come by passing an instance of the implementation. By flipping the parameters, you can use partial application to create type-specific functions. Here, we follow the use of the interface, but we could also use statically resolved type parameters to structurally infer different implementations.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
open System

type Shape<'A> =
    abstract member Area : 'A -> double

type Circle = Circle of radius:double
type Rectangle = Rectangle of width:double * length:double

let CircleShape =
    { new Shape<_> with
        member this.Area(Circle(radius)) = Math.PI * Math.Pow(radius, 2.) }
let RectangleShape =
    { new Shape<_> with
        member this.Area(Rectangle(width, length)) = width * length }

let areaOf<'A> (shapeImpl:Shape<'A>) shape = shapeImpl.Area(shape)
namespace System
type Shape<'A> =
  interface
    abstract member Area : 'A -> double
  end

Full name: Script.Shape<_>
abstract member Shape.Area : 'A -> double

Full name: Script.Shape`1.Area
Multiple items
val double : value:'T -> double (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.double

--------------------
type double = Double

Full name: Microsoft.FSharp.Core.double
Multiple items
union case Circle.Circle: radius: double -> Circle

--------------------
type Circle = | Circle of radius: double

Full name: Script.Circle
Multiple items
union case Rectangle.Rectangle: width: double * length: double -> Rectangle

--------------------
type Rectangle = | Rectangle of width: double * length: double

Full name: Script.Rectangle
val CircleShape : Shape<Circle>

Full name: Script.CircleShape
val this : Shape<Circle>
abstract member Shape.Area : 'A -> double
val radius : double
type Math =
  static val PI : float
  static val E : float
  static member Abs : value:sbyte -> sbyte + 6 overloads
  static member Acos : d:float -> float
  static member Asin : d:float -> float
  static member Atan : d:float -> float
  static member Atan2 : y:float * x:float -> float
  static member BigMul : a:int * b:int -> int64
  static member Ceiling : d:decimal -> decimal + 1 overload
  static member Cos : d:float -> float
  ...

Full name: System.Math
field Math.PI = 3.14159265359
Math.Pow(x: float, y: float) : float
val RectangleShape : Shape<Rectangle>

Full name: Script.RectangleShape
val this : Shape<Rectangle>
val width : double
val length : double
val areaOf : shapeImpl:Shape<'A> -> shape:'A -> double

Full name: Script.areaOf
val shapeImpl : Shape<'A>
val shape : 'A

More information

Link:http://fssnip.net/7Tr
Posted:7 years ago
Author:Ryan Riley
Tags: trait