Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructors with variable number of arguments

Using a few extensions, I can do something like this:

{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}

type family TF (a :: Bool) where
  TF 'True = Int
  TF 'False = Double

data D a where
  D :: TF a -> D a

Notice the constructor D can work in two ways. Either:

D :: Int -> D 'True

or

D :: Double -> D 'False

Using this pattern, I can completely change the arguments to the constructor depending on it's type, whilst reusing it's name.

However, I'd also like the number of arguments to depend on it's name.

I know I could just replace some arguments with () or Void but I'd rather remove them entirely.

Is there anyway to do this?

like image 354
Clinton Avatar asked Jun 06 '17 03:06

Clinton


People also ask

Can constructor take any number of arguments?

If we don't define a constructor in a class, then the compiler creates a default constructor(with no arguments) for the class. And if we write a constructor with arguments or no arguments then the compiler does not create a default constructor.

What is a variable number of arguments?

To call a function with a variable number of arguments, simply specify any number of arguments in the function call. An example is the printf function from the C run-time library. The function call must include one argument for each type name declared in the parameter list or the list of argument types.

How many arguments does parameterized constructor contain?

Constructors that can take at least one argument are termed as parameterized constructors.

Can a constructor have more than one parameter?

The technique of having two (or more) constructors in a class is known as constructor overloading. A class can have multiple constructors that differ in the number and/or type of their parameters. It's not, however, possible to have two constructors with the exact same parameters.


1 Answers

I'm not sure if this is exactly what @luqui had in mind in the comment above, but you can create a smart constructor in a type class that dispatches on the type of its first argument (or any fixed number of initial arguments) to determine the count and types of remaining arguments via a type family:

{-# LANGUAGE TypeFamilies #-}

data D = Int2 Int Int
       | Double1 Double
       deriving (Show)

class D1 a where
  d :: a -> T2 a
type family T2 a where
  T2 Int = Int -> D
  T2 Double = D

instance D1 Int where
  d x = \y -> Int2 x y     -- or just `d = Int2`
instance D1 Double where
  d x = Double1 x          -- or just `d = Double1`

After which:

> d (2 :: Int) 3
Int2 2 3
> d (2 :: Double)
Double1 2.0
> 

It's not clear to me how to generalize this to a situation that requires conditional dispatch:

data D = IntStringDouble Int String Double
       | Int2 Int Int
       | Double1 Double
       deriving (Show)

since you'd need something like:

T2 Int = a -> T3 a

with T3 an additional type family for the next step in the dispatch, and I don't think there's any way to have the RHS of a type family be polymorphic. Maybe someone else can see a way.

Anyway, it's all probably more trouble than it's worth.

like image 69
K. A. Buhr Avatar answered Oct 21 '22 10:10

K. A. Buhr