There is unsurprisingly a run time exception thrown by the following code :
data Necklace = InvalidNecklace |
Necklace { necklace_id :: Int, meow :: Int, ... }
necklace_id InvalidNecklace
Is there some natural way to define a value for necklace_id
when applied to InvalidNecklace
to take a value rather than throwing an exception?
GHC fails with a multiple declarations error for `necklace_id' if I try the obvious thing :
necklace_id InvalidNecklace = -1
Is there perhaps some pragma that'll tell GHC to replace it's inferred declaration by this declaration?
I could declare InvalidNecklace
to be a record by adding { necklace_id :: Int }
, but afaik I cannot guarantee it always returns -1, and generally makes a horrible mess. I could simply define :
get_necklace_id InvalidNecklace = -1
get_necklace_id x = necklace_id x
but this partially defeats the purpose of records.
I suppose one could create a special invalidNecklace
value by writing :
invalidNecklace = Necklace { necklace_id = -1,
meow = error "meow invalidNecklace accessed", ... }
Are there any disadvantages to this second approach? I certainly lose the ability to make meow
strict or unpacked, but perhaps separate debugging and optimized versions could be maintained. Is there a pragma to locally disable warnings for partially initialized records?
(UPDATED BELOW)
As you discovered, the getter defined by the
Necklace
declaration cannot be further defined. There is no pragma to change this.The common practice in Haskell would be to use this style:
Using a magic return value of "-1" is common style in C-type languages with simpler type systems. But note that
Maybe Int
is isomorphic toNecklace
, so it adds little in the simplest case (except access to the large number of common functions for handlingMaybe
that may not exist forNecklace
). If you makeNecklace
more complicated thenget_necklace_id
makes sense.For larger projects it is possible to have template Haskell or an extra tool automatically create the
get_necklace_id
above.UPDATE: Using
fromJust
is not a particularly good idea. To get "reasonable defaults" and "no failure modes" you may compose theget_necklace_id :: Necklace -> Maybe Int
withData.Maybe.fromMaybe :: a -> Maybe a -> a
(one of the common Maybe handling functions) like this:The
a_necklace_id
is identical to your function that replaces InvalidNecklace with (-1). Code that needs a different default can usefrom_necklace_id
.