Giter Site home page Giter Site logo

row-types's Introduction

Row-Types

Build Status Hackage

Row-types is a library of open records and variants for Haskell using closed type families and type literals (among other things...). See examples/Examples.lhs for a literate Haskell file that functions as an overview of how this library can be used, and check out the website for further examples.

Available on Hackage.

This work is a branch from CTRex [1,2] with other inspiration from data-diverse [3]. My thanks to the authors and contributors of those libraries!

[1] https://wiki.haskell.org/CTRex
[2] https://hackage.haskell.org/package/CTRex/docs/Data-OpenRecords.html
[3] https://hackage.haskell.org/package/data-diverse

row-types's People

Contributors

aavogt avatar ak3n avatar alexbiehl avatar atzeus avatar dwincort avatar strake avatar ulfnorell avatar yanok avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

row-types's Issues

AllUniqueLabels (V.Map (Either String) r)

I am running in a problem. I want to write this code:

instance CandidRow r => Candid (V.Var r)
instance CandidRow r => CandidVal (V.Var r) where
    asType = VariantT $ fieldOfRow @r

    toCandidVal' v = VariantV (unescapeFieldName t) val
      where (t, val) = V.eraseWithLabels @Candid toCandidVal v

    fromCandidVal' (VariantV f v) = do
        needle <-
            V.fromLabels @Candid $ \l -> do
                guard (f == unescapeFieldName (R.toKey l))
                return $ fromCandidVal v
            <|> Left ("Unexpected variant tag " ++ show (pretty f))
        V.sequence needle
    fromCandidVal' v = Left $ "Unexpected " ++ show (pretty v)

The point is that I want V.fromLabels to use the Alternative instance of Either String to pick the right field (based on the f). But I want to avoid decoding failures within fromCandidVal to cause backtracking here!

So inside fromLabels I return the pure fromCandidVal v, and only after the right label was found, I want to perform that action. This calls for V.sequence, it seems.

But now I get

    • Could not deduce: AllUniqueLabels (V.Map (Either String) r)
        arising from a use of ‘V.fromLabels’
      from the context: CandidRow r

where

type CandidRow r = (Typeable r, AllUniqueLabels r, Forall r Candid)

It seems that AllUniqueLabels r does not imply AllUniqueLabels (V.Map f r)… is there a way to get that?

Ah, it seems that there is fromLabelsMapA for records, but not for variants. Is there a deep reason for that?

Show instance for Rec interacts with Show instance for lists in a weird way

Steps to reproduce:

  1. Build a record with a least two fields.
> r = #a .== "a" .+ #b .== "b"
  1. If I use it alone, Show works nicely
> r
#a .== "a" .+ #b .== "b"
  1. If I make a singleton list, things start to get strange:
> [r]
[#a .== "a"] .+ ]#b .== "b"]
  1. It gets worse for longer lists:
> [r,r]
[#a .== "a",#a .== "a"] .+ ]#b .== "b"] .+ ,#a .== "a"] .+ ]#b .== "b"]#b .== "b",#a .== "a"] .+ ]#b .== "b"]
> [r,r,r]
[#a .== "a",#a .== "a",#a .== "a"] .+ ]#b .== "b"] .+ ,#a .== "a"] .+ ]#b .== "b"]#b .== "b",#a .== "a"] .+ ]#b .== "b"] .+ ,#a .== "a",#a .== "a"] .+ ]#b .== "b"] .+ ,#a .== "a"] .+ ]#b .== "b"]#b .== "b",#a .== "a"] .+ ]#b .== "b"]#b .== "b",#a .== "a",#a .== "a"] .+ ]#b .== "b"] .+ ,#a .== "a"] .+ ]#b .== "b"]#b .== "b",#a .== "a"] .+ ]#b .== "b"]

trial Left result

Why trial function put successful result into Left instead of Right?
Wouldn't be Right more convenient because of e.g. default Monad instance?

Q: How does Data.Row.Records compare to other extensible record approaches?

For example, does compile time suffer exponentially, having ~ 1+ minute builds past 8-10 fields similar to turingjump/bookkeeper#13 and agrafix/superrecord#12? Or has row-types managed to avoid that particular issue?

If one of you are willing, I'd love to read a comprehensive comparison to some other approaches to find out the strengths and limitations of this implementation. The most efficient way I know how to compare to most other packages at once is to fill in cells in this Google Sheet.

I've added row-types at row 33, under the section titled "Looking for reports:".
Is one of you up for filling in some cells there? Maybe @strake, @atzeus, @dwincort, or someone else?

p.s.: I've heard of this package from this talk. Some more background can be gleaned from this reddit thread.

Allow label in pattern matching?

I've been reading the Example.hs file, and want to share some quick thoughts. This is a low priority ticket on my part, feel free to only respond to this days or weeks from now at your leisure. I'm writing now because I don't want to forget about it.

So, reading this section:

get2D'' :: (r  "x" .== Double .+ "y" .== Double, Disjoint r rest)
        => Rec (r .+ rest)
        -> Rec r
get2D'' ((Label :: Label "x") :== n1 :+ (Label :: Label "y") :== n2 :+ _)
          = #x .== n1 .+ #y .== n2

It would indeed be much nicer if we could just write:

get2D'' :: (r  "x" .== Double .+ "y" .== Double, Disjoint r rest)
        => Rec (r .+ rest)
        -> Rec r
get2D'' (#x :== n1 :+ #y :== n2 :+ _)
          = #x .== n1 .+ #y .== n2

Trying to do this now, we get:

> :t \(#a) -> 1
<interactive>:1:3: error: Parse error in pattern: #a

A parse error. So, it might be quite trivial to fix this upstream in GHC's parser, just make #a de-sugar to (Label :: Label "a") in a pattern context as well. And I am guessing/hoping/wondering that GHC folks would be very happy to take this contribution. I've briefly looked if there may be a trac ticket even open about this, but I haven't found any yet. So maybe the only reason this isn't working is because no one was inconvenienced by it enough to fix it or raise it.

What do you think?

Segmentation fault after upgrading to row-types-1.0

Quite late to the party, but it is about time I upgrade to row-types-1.0

I tried to do it, including some cargo-culting around metamorph, but now the test suite segfaults…

To reproduce, run cabal run test in this commit
nomeata/haskell-candid@aeb1bce (part of branch row-type-1.0).

Did I do something stupid when changing metamorph? Or am I somewhere else relying on internals of row-types that have changed?

Probably the issue isn’t with your code, but thanks for your help nevertheless.

`row-types-aeson` with `aeson-2` compatibility

At the moment it does not build:

src/Data/Row/Aeson.hs:29:21: error:
    • Couldn't match type: unordered-containers-0.2.19.1:Data.HashMap.Internal.HashMap
                             s0 Value                                 
                     with: Data.Aeson.KeyMap.KeyMap Value
      Expected: Rec r -> Object    
        Actual: Rec r                                           
                -> unordered-containers-0.2.19.1:Data.HashMap.Internal.HashMap 
                     s0 Value
    • In the second argument of ‘(.)’, namely
        ‘Rec.eraseToHashMap @ToJSON toJSON’
      In the expression: Object . Rec.eraseToHashMap @ToJSON toJSON
      In an equation for ‘toJSON’:
          toJSON = Object . Rec.eraseToHashMap @ToJSON toJSON
   |
29 |   toJSON = Object . Rec.eraseToHashMap @ToJSON toJSON
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/Data/Row/Aeson.hs:36:59: error:
    • Couldn't match expected type ‘Key’ with actual type ‘Text’
    • In the second argument of ‘(.:)’, namely ‘(show' l)’
      In a stmt of a 'do' block: x <- o .: (show' l)
      In the expression:
        do x <- o .: (show' l)
           x `seq` pure x
   |
36 |     r <- Rec.fromLabelsA @FromJSON $ \ l -> do x <- o .: (show' l)
   |                                                           ^^^^^^^

src/Data/Row/Aeson.hs:48:66: error:
    • Couldn't match expected type ‘Key’ with actual type ‘Text’
    • In the second argument of ‘(.:)’, namely ‘(show' l)’
      In the expression: o .: (show' l)
      In the second argument of ‘($)’, namely ‘\ l -> o .: (show' l)’
   |
48 |   parseJSON (Object o) = Var.fromLabels @FromJSON $ \ l -> o .: (show' l) 
   |                                                                  ^^^^^^^

`ToNativeExact t ρ` as type family

I am playing around with row-types; neat work.

I want to connect it to some other generic library (not shared yet), but I am facing a wrinkle. ToNativeExact t ρ is a type class. I believe I could really use a type family that goes from the generic Rep t type to a Row r. Would that be possible?

Is it possible to deduce lens over constrained type?

(#x .== 4 :: Subset ("x" .== Integer) t => Rec t) .! #x
     Could not deduce: t1 ~ 'R '[ "x" ':-> Integer]
      from the context: Subset ('R '[ "x" ':-> Integer]) t
        bound by the inferred type of
                   it :: Subset ('R '[ "x" ':-> Integer]) t => Rec t
        at <interactive>:12:1-49
      or from: Subset ("x" .== Integer) t1
        bound by an expression type signature:
                   forall (t1 :: Row *). Subset ("x" .== Integer) t1 => Rec t1
        at <interactive>:12:14-48
      t1 is a rigid type variable bound by
        an expression type signature:
          forall (t1 :: Row *). Subset ("x" .== Integer) t1 => Rec t1
        at <interactive>:12:14-48
      Expected type: Rec t1
        Actual type: Rec ("x" .== Integer)
     In the expression:
        (#x .== 4 :: Subset ("x" .== Integer) t => Rec t)
      In an equation for it’:
          it = (#x .== 4 :: Subset ("x" .== Integer) t => Rec t)

But

(#x .== 4 :: (Subset ("x" .== Integer) t, t ~ ("x" .== Integer)) => Rec t) .! #x
4

Design help with default values

I'd like to request help for designing a solution I'm building for integration test factories together with row-types.

Here's a self-containing repo which can be built/launched via stack run https://github.com/k-bx/row-types-default/blob/e6a2b5c03d419038d3fb5301251c2aca71f72d63/src/Main.hs

The two problems I'm trying to solve now:

First, how do I make ability to make parameters implicit with default value to be Nothing? Instead of calling buildProduct (#name .== Nothing .+ #companyId .== Nothing) I'd like to only be required to mention those parameters which are non-Nothing (which I want to over-write from default).

Second, what would be a good way to pass parameters of buildCompany to buildProduct under a name #company? E.g., I'd like my API to look like this at the end:

main = do
  product' <- buildProduct (def (#company .== def (#name .== "My Company")))
  ...

and this would build everything with default values, except for product's company name to be "My Company".

Hope this sounds clear. Thanks!

glue Rec's

Any type family to .+ Rec's, not Row's only?
(Or to get Row type of Rec type)

Should the aeson instances omit `Nothing` fields?

When generating instances, aeson can be configured to omit Nothing fields. At the moment the instances for Rec don't do this: Nothing gets serialized as null and you can't omit such fields in the JSON. It would be nice to have a way to express records with fields that can be missing.

I don't really know what to do about this: both seem sensible, and in aeson this is configurable, and arguably it's a bit weird to treat Maybe specially like this, see: haskell/aeson#646

Feature request: unions aka corecords

Would it make sense to have not-tagged sums (like).
Also pls consider to use .* instead of .+ (in way like Caml using * in types) for records, and keep .+ for variants/unions.

⚠️ Archival Notice

This repository will be archived in 30 days in accordance with Target's internal retention policy.

Archived projects become read-only and will remain accessible to the public.
If you have questions or concerns, reach out here or internally to the OSPO team.

Soliciting feedback

To any users of row-types, I'm playing around with some breaking changes and am looking for feedback. The changes are breaking enough that I'm planning on releasing the next version as 1.0.0.0 so anyone with any limits on forward-compatibility won't have a big problem, but I'd still value users' opinions.

I have four basic questions:

  1. Have you ever used Forall or metamorph in your code?

  2. Have you ever used the unsafe record or variant functions outside of a call to metamorph?

  3. Have you run into issues where GHC seemed really stupid about constraints (where you needed to add a seemingly obvious constraint like Extend l t r .! l ≈ t)? If so, do you have any examples?

  4. Do you think it's silly that trial, multiTrial, and split (all of which return an Either) all put the "successful" result in Left and the failure in Right?

Poly-Kinded Type Families

I noticed that many of the type families for Data.Row.Internal only operate on rows of kind Row *, but it seems like it would be just as useful to use these type families (in particular, (.==) and (.+)) for rows of any kind, since row types can be used for more than just records and variants.

I have a working version here:
https://github.com/jqyu/graphql-row-types/blob/master/src/Data/Row/Poly.hs
and I would be happy to make a PR if you think this is the right approach (:

Is there an easy way to make functor instances?

I hope this is an okay place to ask a question.

Basically, if I have a row type, say, "a" .== Foo x .+ "b" .== Bar x, and Foo and Bar are both Functors (or any class, really), is there an easy way to map over the entire structure and get a result of "a" .== Foo y .+ "b" .== Bar y?

I've been trying to use map and transform, but have had no success using them so far (my type-level experience is limited).

Thanks in advance!

Feature request: named function's arguments

With row-types it's possible to pass some named parameters to function.
But what about partial application? Would it make sense to implement .-> type indexed by parameter name? "Int .-> "x" Float .-> "y" () (Or? ("x", Int) .-> ("y", Float) .-> ())

Additional feature: optional parameters.
E.g. via some typeclass mark some field as optional, wrap them to Maybe if missed.
Optional '["x"] => Rec ("x" .== Int .+ "y" .== Float) -> (). Though not sure here. It must behave like Rec ("y" .== Float) -> () for caller, and Rec ("x" .== Maybe Int .+ "y" .== Float) -> () for function implementation body.
"Int .?> "x" Float .-> "y" () (Or? ("x", Int) .?> ("y", Float) .-> ())

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.