Search code examples
scalahaskelltype-constraintshigher-kinded-types

Translate a sample from Haskell to Scala (HKT, type constraints)


I'm learning Functional Programming, I made this sample in Haskell it works like I want, but when I don't know how to do such constraint in Scala, I don't understand how I can use HKT and Constraints in Scala atm.

{-# LANGUAGE GADTs #-}

module Complex
  ( Complex
  , add
  ) where

data Complex a where
  Complex :: (Show a, Fractional a) => a -> a -> Complex a

instance Show (Complex a) where
  show (Complex a b) = "z = " ++ show a ++ " + i * " ++ show b

add :: Complex a -> Complex a -> Complex a
add (Complex a b) (Complex c d) = Complex (a + c) (b + d)

Thanks in advance :)

So, I did it but it seems kinda wrong, can I make It better ?

  case class ComplexNumber[T](realPart: T, imagPart: T){
    override def toString: String = s"z= $realPart + $imagPart i"
  }

  object ComplexNumber {
    def add[T](a: ComplexNumber[T], b: ComplexNumber[T])(implicit evidence: Numeric[T]): ComplexNumber[T] = {
      ComplexNumber(evidence.plus(a.realPart, b.realPart), evidence.plus(a.imagPart, b.imagPart))
    }
  }

It seems strange I have to use this evidence helper..


Solution

  • The "evidence helper" guarantees that type parameter T is limited to numeric types that can be added together (or any other arithmetic operation).

    It can be hidden behind some syntactic sugar making T a "context bound" type parameter. You can also bring in some extra implicits to make the addition syntax more natural.

    object ComplexNumber {
      import Numeric.Implicits._  //can be placed elsewhere in the file
      def add[T: Numeric](a: ComplexNumber[T], b: ComplexNumber[T]):ComplexNumber[T] =
        ComplexNumber(a.realPart + b.realPart, a.imagPart + b.imagPart)
    }
    

    You can also go for a more natural client-side syntax.

    import Numeric.Implicits._
    case class ComplexNumber[T: Numeric](realPart: T, imagPart: T){
      override def toString: String = s"z= $realPart + $imagPart i"
      def +(that: ComplexNumber[T]):ComplexNumber[T] =
        ComplexNumber(this.realPart + that.realPart, this.imagPart + that.imagPart)
    }
    

    Usage:

    ComplexNumber(8,1) + ComplexNumber(4,4)  //res0: ComplexNumber[Int] = z= 12 + 5 i