-
Notifications
You must be signed in to change notification settings - Fork 107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ #206 ] Deriving FromField/ToField instances #207
base: master
Are you sure you want to change the base?
Conversation
Hello! Any thoughts on this? |
Friendly ping |
386a896
to
6be39c0
Compare
Works only for the following representations: * Single nullary constructor By default encodes constructor name * Sum with nullary or unary constructors Encoding is similar to 'UntaggedValue' encoding from 'aeson'
Sorry for the slow reaction. I rebased this onto
|
Adding 'pure' to imports fixed the problem |
I moved this to the conditional import of |
I see your parser supports untagged unions via Lines 78 to 86 in fd168df
I added a performance test for parsing unions that should take O(2ˆn) wrong branches until it succeeds. I only played it to Also, could you draft a CHANGELOG entry describing the new features? |
I totally agree that performance is important, so will add benchmarks. Though I don't think extreme cases like you provided are a real problem unless there is a significant performance loss that is "unexpected" and can be avoided. As example of "unexpected" performance loss: parsing of enum happens to be O(n^2) instead of O(n), where Encoding for sums with unary constructors is somewhat fishy for another reason similar to UntaggedValue:
This may be confusing but IMHO parsing sums with unary constructors is useful, e.g. Let me add some benchmarks and than we can come back to the discussion. Any ideas of what else to benchmark beside for "branched" unions?
Sure thing, but I think it makes sense to do after we resolve opened question. |
I agree to your argumentation. Banning untagged unions altogether just because there are problematic cases would throw out the baby with the bathwater. So, let's include them. |
TODO
|
2721f0d
to
82504fc
Compare
I cleaned up TODO list above, so duplicating myself here
Below is the most recent set of benchmarks. @@ -775,8 +775,9 @@ genericParseField
genericParseField opts field = Parser $ \onFailure onSuccess ->
unParser (gParseField opts field) (\_ -> onFailure err) (onSuccess . to . M1)
where
- err = "Can't parseField of type " <> datatypeName (Proxy :: Proxy meta d f)
- <> " from " <> show field
+ err = "fail"
+ -- err = "Can't parseField of type " <> datatypeName (Proxy :: Proxy meta d f)
+ -- <> " from " <> show field
{-# INLINE genericParseField #-}
-- | A type that can be converted to a single CSV field.
@@ -1405,7 +1406,7 @@ instance (Constructor c) => GFromField (C1 c U1) where
gParseField opts field = Parser $ \onFailure onSuccess ->
if field == expected
then onSuccess val
- else onFailure $ "Can't parse " <> show expected <> " from " <> show field
+ else onFailure "Fail" -- $ "Can't parse " <> show expected <> " from " <> show field
where
expected = encodeConstructor opts val
val :: C1 c U1 p Llist of open questions I need your opinion about (much duplicates TODO):
Few side notes (posting here not to forget to discuss separately whether it makes sense to investigate further):
With best regards! |
benchmark-iut.csv |
Also reordered things a bit
I've carefully revisited things and now almost sure that my original understanding was wrong. The purpose of Beside for that I've added generic instances for uninhabited types ( Just to summarize - supported representations: -- 1. Uninhabited types: implementation is absurd, but it is expected that such terms never exist.
-- Still they may be useful when one needs to statically guarantee non-existence of term:
-- e.g., "Maybe Void" can exist only as "Nothing" (ignoring bottom)
-- An example from practice
data Foo (p :: Bool) = Foo
{ foo :: Maybe (Restricted p)
, ... }
type family Restricted (p :: Bool) where
Restricted True = Int
Restricted False = Void
-- It's ofc a simplified example and this could be modeled with passing type explicitly,
-- but that is not always feasible for other reasons.
-- 2. Nullary constructor types
data Foo0 = Foo0
-- toField Foo0 == "Foo0"
-- 3. Unary constructor types
-- This was absent in original implementation, but unlike with JSON there is no ambiguity,
-- so see no sense to not provide instance for them.
-- In JSON `data Foo a = Foo{foo :: a}` can be reasonably encoded
-- both as `toJSON a` and `object ["foo" .= toJSON a]`.
newtype Foo1 a = Foo1 a
-- toField (Foo1 a) == toField a
-- 4. Sum with branches fitting into 2. or 3.
data FooSum a = FooA | FooB a
-- toField FooA == "FooA"
-- toField (FooB a) == toField a |
Just in case: I'm waiting for some feedback |
Friendly ping |
Works only for the following representations:
By default encodes constructor name
Encoding is similar to 'UntaggedValue' encoding from 'aeson'