Reputation: 10041
I have something similar to the following
data A = A
{ id :: Integer
, foo :: Maybe String
, bar :: Maybe String
, baz :: Maybe String
}
This data is coming in to my service as JSON. This request is only considered valid when one or more of foo
, bar
, or baz
are given. Is there a better way to express this within Haskell's type system?
Note: Unfortunately I am unable to make this separate requests. I'm just following a defined protocol.
Upvotes: 7
Views: 164
Reputation: 152697
Consider giving one branch for each possible required field:
data A
= Foo
{ foo :: String
, barM, bazM :: Maybe String
}
| Bar
{ bar :: String
, fooM, barM :: Maybe String
}
| Baz
{ baz :: String
, fooM, barM :: Maybe String
}
It's a fair bit of boilerplate, but it's very direct and quite clear about what's required.
Upvotes: 2
Reputation: 120711
Expanding on ʎǝɹɟɟɟǝſ's suggestion to use a map: there's also a type specifically for non-empty maps. (Note however that this sort of clashes with the more popular nonempty-list type from the semigroups
library.)
import qualified Data.NonEmpty.Map as NEM
data Field = Foo | Bar | Baz
data A = A { id :: Integer
, fields :: NEM.T Field String
}
Upvotes: 2
Reputation: 76240
I would use a Map Field String
with data Field = Foo | Bar | Baz
(this can easily be replaced with String
if needed, and then have:
data A = A
{ id :: Integer
, fields :: Map Field String
}
Now checking for the validity condition is as simple as:
isValid :: A -> Bool
isValid = not . Map.null . fields
Upvotes: 4
Reputation: 2869
If it is not mandatory to have three separate fields with foo,bar and baz, I'd go with this, NonEmpty guarantees that there is at least one element, though there can of course be more.
import Data.List.NonEmpty
data Impression = Banner String | Video String | Native String
data A = A
{ id :: Integer
, fooBarBaz :: NonEmpty Impression
}
Upvotes: 4
Reputation: 120711
http://hackage.haskell.org/package/these-0.4.2/docs/Data-These.html
import Data.These
data A = A
{ id :: Integer
, fooBarBaz :: These Foo (These Bar Baz)
}
type Foo = String
type Bar = String
type Baz = String
Upvotes: 11