I have this datatype
newtype Dimension a b = MkDimension b
Whenever it is properly formatted (e.g. satisfies theformat constraint), I want to derive a set of classes. I do not want aDimension which does not satisfy theformat constraint to be created.
This also means that if I do this:
newtype Dimension a b = MkDimension b deriving newtype NumfromIntegral is still capable of producing dimensions which are improperly formatted. What should I do? I want to also deriveapplicative,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 thatfromIntegral is only allowed on properly formatted dimensions.
type family Has0 a where Has0 '[] = 'False Has0 '(_,0) ': _ = 'True Has0 _ ': b = Has0 btype Format a = (a ~ Sort a, Has0 a ~ 'False)The definition of sort is too long to fit in here.
- Say a bit more about what
Dimensionrepresents. 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 notNum. People do that all to often, and it subverts the whole point of having a strong type system that makes it easy to getjust the right results, instead of weird unexpected behaviour.leftaroundabout– leftaroundabout2025-09-08 19:21:38 +00:00CommentedSep 8 at 19:21 - You haven't told us anything about what "the
formatconstraint" means. But in general I doubt thatderivingis going to be sufficient for you; you will almost certainly have to do those derivations manually.Louis Wasserman– Louis Wasserman2025-09-08 19:42:17 +00:00CommentedSep 8 at 19:42 - I added the definition of format and explained what I wanted it to do.Ashok Kimmel– Ashok Kimmel2025-09-08 19:50:53 +00:00CommentedSep 8 at 19:50
- 1Ok, so this type shoulddefinitely not have
Numinstance - it would be completely the wrong interface. Instead, you should implementVectorSpaceplus a custom multiplication, that takes care so e.g. the product of two lengths is an area (different type).leftaroundabout– leftaroundabout2025-09-08 20:31:44 +00:00CommentedSep 8 at 20:31
1 Answer1
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.
3 Comments
VectorSpace is the appropriate abstraction for this kind of thing. It's a lightweight dependency, no reason not to use it. If you find thea ^+^ b syntax ugly, you can alwaysimport Prelude hiding ((+), (-)) and locally defineinfixl 6 +; (+)=(^+^).Explore related questions
See similar questions with these tags.