6

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?

Clinton
  • 22,361
  • 15
  • 67
  • 163
  • 3
    I don't think you can do this with a *constructor* specifically, since the final codomain must be syntactically the data type you are defining. But if you make a function which constructs `D`s I think you can, by making the codomain a type family – luqui Jun 06 '17 at 04:30
  • 1
    This looks as if it would make type inference even harder than it is, for little gain. You can use `D :: F a -> D a` where `F` returns a tuple type of different length depending on `a`. It requires more boxing, but not that much. – chi Jun 06 '17 at 07:55
  • Why do you want to do this? – Benjamin Hodgson Jun 06 '17 at 18:01

1 Answers1

1

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.

K. A. Buhr
  • 45,621
  • 3
  • 45
  • 71