1

I have this datatype

newtype Dimension a b = MkDimension b

Whenever it is properly formatted (e.g. satisfies the format constraint), I want to derive a set of classes. I do not want a Dimension which does not satisfy the format constraint to be created.

This also means that if I do this:

newtype Dimension a b = MkDimension b 
    deriving newtype Num

fromIntegral is still capable of producing dimensions which are improperly formatted. What should I do? I want to also derive applicative, monoid, and others which would all give the opportunity to create invalid dimension values.

Details: To ensure that ordering doesn't matter when adding dimensions, I attempted to ensure that a Dimension would never have anything to the 0, and would be fully sorted (e.g. (MkDimension 2 :: Dimension '[ '("Meter",0)] Int) + (MkDimension 4 :: Dimension '[] Int)) would be bad, as they are the same dimension, but we would get a type-error. More realistically, the types could be in the wrong order, also causing an error. (MkDimension 2 :: Dimension '[ '("Meter",1),'("Foot",1)] Int) + (MkDimension 4 :: Dimension '[ '("Foot",1),'("Meter",1)] Int). To stop both of those, I want to make it so that fromIntegral is only allowed on properly formatted dimensions.

type family Has0 a where 
    Has0 '[] = 'False
    Has0 '(_,0) ': _ = 'True
    Has0 _ ': b = Has0 b

type Format a = (a ~ Sort a, Has0 a ~ 'False)

The definition of sort is too long to fit in here.

4
  • Say a bit more about what Dimension represents. It's hard to answer right now. Generally, I would recommend to avoid the temptation to derive All tEh kLaSseS for your custom data types, and in particular not Num. People do that all to often, and it subverts the whole point of having a strong type system that makes it easy to get just the right results, instead of weird unexpected behaviour. Commented Sep 8 at 19:21
  • You haven't told us anything about what "the format constraint" means. But in general I doubt that deriving is going to be sufficient for you; you will almost certainly have to do those derivations manually. Commented Sep 8 at 19:42
  • I added the definition of format and explained what I wanted it to do. Commented Sep 8 at 19:50
  • 1
    Ok, so this type should definitely not have Num instance - it would be completely the wrong interface. Instead, you should implement VectorSpace plus a custom multiplication, that takes care so e.g. the product of two lengths is an area (different type). Commented Sep 8 at 20:31

1 Answer 1

2

The answer to the question as asked is to use standalone deriving.

{-# Language StandaloneDeriving #-}
deriving instance Format a => Num (Dimension a b)

However, I agree with the comments that this probably isn't wise; especially the type of (*) is probably not sensible in that instance.

Sign up to request clarification or add additional context in comments.

3 Comments

What should I use then? Num is a mess, but I want addition and subtraction using the common operators without needing special prelude imports.
As I commented earlier, VectorSpace is the appropriate abstraction for this kind of thing. It's a lightweight dependency, no reason not to use it. If you find the a ^+^ b syntax ugly, you can always import Prelude hiding ((+), (-)) and locally define infixl 6 +; (+)=(^+^).
Are there any common type family-based multiplication functions in use?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.