I'm working on an idea to stitch together lens-aeson and higgledy in order to construct types from partial data that we parse from aeson. Right now I have, as an example:
{-# LANGUAGE DataKinds #-}{-# LANGUAGE DeriveGeneric #-}{-# LANGUAGE TypeApplications #-}moduleLibwhereimportControl.Lenshiding((.=))importData.AesonimportData.Aeson.LensimportData.Functor.ConstimportData.Functor.IdentityimportData.Generic.HKDimportData.MaybeimportData.MonoidimportGHC.GenericsdataDepartment=Sales|Engineering|Management|Supportderiving(Eq,Generic,Show)instanceToJSONDepartmentinstanceFromJSONDepartmentdataEmployee=Employee{_employeeFirstName::String,_employeeLastName::String,_employeeNumber::Int,_employeeDepartment::Department}deriving(Eq,Generic,Show)typeEmployeeFf=HKDEmployeeftypePartialEmployee=EmployeeFLastparseEmployee::Value->PartialEmployeeparseEmployeev=mempty&field@"_employeeFirstName".~(v^.key"firstName"._JSON)
What I'm trying to figure out is how to get parseEmployee to have a type error if the lens focused on by the setter returns a value of the wrong type. For example:
𝝀 let empObj = object [ "firstName" .= 123, "somethingElse" .= 123 ]
𝝀 safeParseEmployee empObj
Employee {_employeeFirstName = Last {getLast = Nothing}, _employeeLastName = Last {getLast = Nothing}, _employeeNumber = Last {getLast = Nothing}, _employeeDepartment = Last {getLast = Nothing}}
It would be better if, as aeson would, we have this become a type error. :thinking:
No progress yet on getting a type error to show up but I have been able to be more specific by using the right prism, _String which actually returns Text... a bit of a misnomer.
Actually that does solve the type error issue. Now I just have to figure out how to get a Value into Last which seems to require a Monoid instance. :thinking:
It only seems to mostly work if I use the _JSON prism -- _String and _Integer etc cause problems... the consequence is that if the type of the value in the parsed Value doesn't match up with what our record expects we simply get Nothing
If I try to use _String it throws a type match error:
• Couldn't match type ‘Last Text’ with ‘Text’
Expected type: Getting (Last Text) Value (Last Text)
Actual type: (Text -> Const (Last Text) Text)
-> Value -> Const (Last Text) Value
• In the second argument of ‘(^.)’, namely ‘key "firstName" . _String’
In the second argument of ‘(.~)’, namely
‘(o ^. key "firstName" . _String)’
In the second argument of ‘(&)’, namely
‘field @"_employeeFirstName" .~ (o ^. key "firstName" . _String)’
|
33 | & field @"_employeeFirstName" .~ (o ^. key "firstName" . _String)
| ^^^^^^^^^^^^^^^^^^^^
But _JSON compiles fine... the consequence is that if there is a type mismatch I get a value of Nothing for the field at run time.
@Jack Henahan That's what it says, though as I've mentioned I'm not sure how to stuff the result of view into the Last
However, it seems like aeson can figure out the generic instance for type MaybeEmployee = EmployeeF Maybe! Which means I we can use decodeEither to recover those type mismatch errors instead of rolling our own parsing with aeson-lens.
{-# LANGUAGE OverloadedStrings #-}{-# LANGUAGE DataKinds #-}{-# LANGUAGE DeriveGeneric #-}{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE TypeApplications #-}{-# LANGUAGE TypeSynonymInstances #-}moduleLibwhereimportControl.Lenshiding((.=))importData.AesonimportData.Aeson.LensimportData.Functor.ConstimportData.Functor.IdentityimportData.Generic.HKDimportData.MaybeimportData.MonoidimportData.Text(Text())importGHC.GenericsdataDepartment=Sales|Engineering|Management|Supportderiving(Eq,Generic,Show)instanceToJSONDepartmentinstanceFromJSONDepartmentdataEmployee=Employee{_employeeFirstName::Text,_employeeLastName::Text,_employeeNumber::Int,_employeeDepartment::Department}deriving(Eq,Generic,Show)typeEmployeeFf=HKDEmployeeftypePartialEmployee=EmployeeFLasttypeMaybeEmployee=EmployeeFMaybeinstanceToJSONPartialEmployeeinstanceFromJSONPartialEmployeeinstanceToJSONMaybeEmployeeinstanceFromJSONMaybeEmployee
I'm working on an idea to stitch together
lens-aeson
andhiggledy
in order to construct types from partial data that we parse fromaeson
. Right now I have, as an example:What I'm trying to figure out is how to get
parseEmployee
to have a type error if the lens focused on by the setter returns a value of the wrong type. For example:It would be better if, as
aeson
would, we have this become a type error. :thinking:Maybe a @Chris Penner question. ;)
No progress yet on getting a type error to show up but I have been able to be more specific by using the right prism,
_String
which actually returnsText
... a bit of a misnomer.Now the trick seems to be finding a way to focus the prism that lets me return into
Last
instead ofMaybe
... I presently have:which isn't quite right... progress!
Actually that does solve the type error issue. Now I just have to figure out how to get a
Value
intoLast
which seems to require aMonoid
instance. :thinking:It only seems to mostly work if I use the
_JSON
prism --_String
and_Integer
etc cause problems... the consequence is that if the type of the value in the parsedValue
doesn't match up with what our record expects we simply getNothing
But yeah; should be able to:
(Make sure you're using
Data.Monoid (Last)
instead of the Semigroup versionLet me know if that's not what you're looking for :)
If I try to use
_String
it throws a type match error:But
_JSON
compiles fine... the consequence is that if there is a type mismatch I get a value ofNothing
for the field at run time.I guess I'd have to handle writing my own type validation.
Something that would let me say, "You gave me an
Int
when I was expecting aString
while parsing the value of the key"foo"
"@James King It looks like it's mainly complaining about the lack of a
Last
in there. Am I reading it wrong?@Jack Henahan That's what it says, though as I've mentioned I'm not sure how to stuff the result of view into the
Last
However, it seems like
aeson
can figure out the generic instance fortype MaybeEmployee = EmployeeF Maybe
! Which means I we can usedecodeEither
to recover those type mismatch errors instead of rolling our own parsing withaeson-lens
.Niiiice
HKD is truly wonderful magic
It's pretty great. I deal with a lot of partial data on my inputs and having HKD is like having super powers.