Reputation: 1133
I want to build a large schema in Haskell. The constituents take parameters and the parameters are constrained. As an example, I might decide that a circle takes one parameter called Radius, which is constrained to be non-negative. I will define the parameters globally, since each can be used by multiple constituents. There may be hundreds of parameters, and many will have long, difficult-to-type names.
I have a solution of sorts, but the parameter declarations are repetitive and I'd like to simplify them. My criteria for "simple" is just to minimize the number of times a parameter name must be typed. One part of this is to simplify the parameter definitions themselves. Another is to avoid typing parameter names when creating data objects, if possible. So, one should be able to construct a Circle without actually typing "Radius".
The code is below, followed by a few more specific questions. Thanks in advance for any help!
data Constraint = Constraint
test :: Float -> Constraint -> Bool
test _ _ = undefined
--
nonnegative :: Constraint
nonnegative = undefined
--
data Expr = Constant Float -- | Variable String | Add Parameter Parameter ...
eval (Constant x) = x
--
class Parameter a where
value :: a -> Float
constraint :: a -> Constraint
validate :: a -> Bool
validate x = test (value x) (constraint x)
-- Schema. Expecting dozens of constituents with many parameters existing
-- in complex relationships.
data Shape = Circle Radius
--
-- There may be hundreds of parameters like Radius, many with long,
-- difficult-to-type names.
data Radius = Radius Expr
instance Parameter Radius where
constraint _ = nonnegative
value (Radius r) = eval r
Can you suggest a better way to structure this code?
I think Template Haskell could be used to define a parameter (like Radius) without repeating the name. Would you recommend that approach?
Is it possible to write a default rule for value? Naively, I want to
match the pattern value (_ x)
, but that's not well-typed, of course.
Is there some way of accomplishing the same thing?
Is there a simpler way to associate a value with a type? For instance,
Radius has a constraint associated with the type, but it seems unnecessary to
have to construct a Radius to get its constraint. When I try to write
constraint :: Constraint
, GHC complains that the type parameter a
is
not used.
Upvotes: 1
Views: 315
Reputation: 60463
It sounds like you wish Haskell had the ability to declare subtypes from predicates. This can be done with smart constructors, but it's true, there is a bit of boilerplate involved. I don't think Template Haskell is such a bad idea. It wouldn't be too hard to wrap up the definition of a smart constructor into something like
subtype "Radius" 'Float [| \x -> x >= 0 |]
You said that there might be hundreds of these things, which raises a design alarm in my mind. I would be looking very hard at this point for opportunities to add more conceptual abstraction to your schema—hundreds is too many. But without more info and context all that's all I can really say: beware!
Upvotes: 1