I am new to the language, trying to write my first non-trivial program. On the way, I am stuck creating an Arbitrary
instance. Yet, I suppose my question to follow points at my general lack of understanding composing several applicative and monadic types. Hence, I hope to gain fundamental insight form the following. Thanks for your help!
I defined an Address
type and a smart constructor with validation of the individual fields as follows:
data Address = Address
{ street :: StreetName
, streetExt :: Maybe StreetName
, city :: CityName
, zipCode :: ZipCode
, country :: CC.CountryCode
} deriving (Eq, Show)
mkAddress :: Text -> Maybe Text -> Text -> Text -> Text -> Maybe Address
mkAddress aStreet aStreetExt aCity aZipCode aCountry =
Address <$> mkStreetName aStreet
<*> Just (aStreetExt >>= mkStreetName)
<*> mkCityName aCity
<*> mkZipCode aZipCode
<*> CC.fromMText aCountry
StreetName
, CityName
and ZipCode
are newtype wrappers for Text
with a validating smart constructor that simply limits the maximum length of these fields. The streetExt
field is optional. The country code uses Data.ContryCodes.CountryCode
.
The overall type is abstract, the defining module only exports the smart constructor but not the data constructor.
I am now trying to create an Arbitrary instance for this type>
instance Arbitrary D.Address where
arbitrary = do
maybeAddress <- D.mkAddress <$> arbitrary -- streetName
<*> return Nothing -- streetExt
<*> arbitrary -- city
<*> arbitrary -- zipCode
<*> elements ["DE", "FR", "AG", "RW"] -- country
return fromJust maybeAddress
However, I am stuck with the following type checker error:
• Couldn't match type ‘Maybe a0 -> a0’ with ‘Gen D.Address’
Expected type: Maybe D.Address -> Gen D.Address
Actual type: Maybe D.Address -> Maybe a0 -> a0
• The function ‘return’ is applied to two arguments,
but its type ‘(Maybe a0 -> a0)
-> Maybe D.Address -> Maybe a0 -> a0’
has only three
From this error, I take that there is some issue wrapping the Generator inside the Maybe, or the other way around. But even after several experiments lift
and join
I can't get it to type check. Thus, I suspect that my mental model is flawed of how the types are wrapped and how the generator monads are returned.
It would be great if someone could point out my mistake. In addition, I would highly appreciate a comment on this type of data modelling with abstract types and smart constructors - is this general recommended practice, or does it lead to problems like the one I am facing?
Thanks very much!
The error message says it (although the subsequent explanation is misleading):
The function ‘return’ is applied to two arguments
You have
return fromJust maybeAddress
You want
return (fromJust maybeAddress)