Home > Enterprise >  Strict type alias in Haskell
Strict type alias in Haskell

Time:09-17

Suppose I have a recursive function taking 3 integers, each having a different meaning, e.g.

func :: Int -> Int -> Int -> SomeType1 -> SomeType2

What I want is to prevent myself from mistyping the order of the arguments like this (somewhere in the func implementation):

func a b c t = f b a c ( someProcessing t ) 

The easiest way I've come up with is to define type aliases like

type FuncFirstArg = Int
type FuncSecondArg = Int
type FuncThirdArg = Int

And change func signature:

func :: FuncFirstArg -> FuncSecondArg -> FuncThirdArg -> SomeType1 -> SomeType2

But it seems like this approach doesn't work as I intended. Why does Haskell still allow me to pass FuncSecondArg as a first argument and so on. Is there a way to do what I want without declaring datatypes?

CodePudding user response:

type in Haskell is a rename of an existing type. Just like String and [Char] are fully exchangeable, so are FuncFirstArg and Int, and by transition FuncSecondArg as well.

The most normal solution is to use a newtype which was introduced exactly for the purpose of what you try to achieve. For convenience, it is good to declare it as a record:

newtype FuncFirstArg = FuncFirstArg {unFuncFirstArg :: Int}

Note that newtype is entirely reduced during compilation time, so it has no overhead on the runtime.


However, if you have many arguments like in your example, a common strategy is to create a dedicated type for all of the parameters supplied to the function:

data FuncArgs = FuncArgs
  { funcA :: Int
  , funcB :: Int
  , funcC :: Int
  , funcT :: Sometype1
  }

f :: FuncArgs -> Sometype2

Yes, it has some bad impact on currying and partial application, but in many cases you can deal with it by providing predefined argument packs or even uncurry the function:

defaultArgs :: Sometype1 -> FuncArgs
defaultArgs t = FuncArgs {a = 0, b = 0, c = 0, t = t}

fUnc :: Int -> Int -> Int -> SomeType1 -> SomeType2
fUnc a b c t = f $ FuncArgs a b c t

Conclusion

For the typechecker to distinguish types, the types have to be actually different. You can't skip defining new types, therefore.

  • Related