GithubHelp home page GithubHelp logo

purescript / purescript-record Goto Github PK

View Code? Open in Web Editor NEW
70.0 11.0 32.0 66 KB

Functions for working with records and polymorphic labels

License: BSD 3-Clause "New" or "Revised" License

PureScript 93.46% JavaScript 6.54%

purescript-record's People

Contributors

chexxor avatar garyb avatar hdgarrood avatar jordanmartinez avatar justinwoo avatar kl0tl avatar kritzcreek avatar liamgoodacre avatar matthewleon avatar natefaubion avatar paf31 avatar paluh avatar thomashoneyman 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

purescript-record's Issues

Add new and run to Record.ST

Foreign.Object.ST and Data.Array.ST export means of creating empty mutable structures without having to thaw an immutable one. Should we also export new :: forall h. ST h (STRecord h ()) from Record.ST for consistency?

Thereโ€™s also Foreign.Object.runST and Data.Array.ST.run to avoid copying the mutable structure in the end. Any reason not to add a similar run :: forall r. (forall h. ST h (STRecord h r)) -> Record r to Record.ST? This would require an unsafeFreeze :: forall h r. STRecord h r -> ST h (Record r) function that we could export with an unsafeThaw :: forall h r. Record r -> ST h (STRecord h r) function from a new Record.ST.Unsafe module.

Argument precedence of Record and Builder merging

I've noticed that the merge-like functions in 'Record' favor their first argument, whereas those in 'Record.Builder' favor their second. I'm guessing that a user would at least initially assume that Record merging and Builder merging would have identical semantics except for the function/Builder difference. Could this semantic distinction and its rationale be documented? Alternatively, could either Record merging or Builder merging be updated so that the two function sets are consistent with each other? (As far as I can tell, there's no pressing problem motivating this change, but consistency is often good for its own sake, and it might help to improve grokkability and to prevent merger-precedence bugs.)

-- Record
merge
  :: forall r1 r2 r3 r4
   . Union r1 r2 r3
  => Nub r3 r4
  => Record r1
  -> Record r2
  -> Record r4
merge l r = runFn2 unsafeUnionFn l r
-- Record.Builder
merge
  :: forall r1 r2 r3 r4
   . Row.Union r1 r2 r3
  => Row.Nub r3 r4
  => Record r2
  -> Builder (Record r1) (Record r4)
merge r2 = Builder \r1 -> runFn2 unsafeUnionFn r1 r2

Is the ST module at all useful?

This may be a case of serious user-error problem here, but it seems to me that Record.ST is only useful in a very specific and probably not common situation: you can only use it to modify the values of existing fields in a record.

This isn't a complaint about thaw being the only way to get an STRecord, I see that there's an unreleased addition of a new - but actually this is kinda useless, since you can't do anything with an STRecord h () aside from freeze or run. You cannot use the ST interface to add or remove fields.

Given that it only allows modification, at the expense of one copy (at a minimum, two if freeze is used rather than run), it seems like this module may as well not exist - you can just modify an object with update syntax or by rebuilding it completely for the same cost.

I don't think it's fixable either - I think it could be done as an indexed monad perhaps, tracking the row in/out of each operation as the indexed part, but then it wouldn't be ST anymore either.

If I'm wrong here, apologies, can someone show me how you'd use this to build a record from scratch. ๐Ÿ˜„

Curry/partially apply a record

@MonoidMusician had a super-cool idea. A function which takes an n-field record as argument is like an n-arity function. If we can partially apply a function, we should be able to partially apply fields of a record.

It should work something like this:

-- If you have some function which takes a record as an argument...
f :: { field1 :: Int, field2 :: Int } -> Int
-- you could partially apply it.
curry f {field1: 1} :: { field2 :: Int } -> Int

Here's the snippet he pasted. He said it needs to be type-checked xD.

foreign import unsafeMergeImpl :: forall r1 r2 r.
  Record r1 -> Record r2 -> Record r

unsafeMerge :: forall r1 r2 r. Union r1 r2 r =>
  Record r1 -> Record r2 -> Record r

class CurryNamed
  (expected :: # Type)
  (given    :: # Type)
  (final    ::   Type)
  (step     ::   Type)
  | expected given final -> step
  where
    curryNamed ::
      (Record expected -> final) ->
      (Record given -> step)

instance curryNamed ::
  ( Union remaining given expected
  , RowToList remaining remainingRL
  , CurryNamedRL remainingRL expected remaining given final step
  ) => CurryNamed expected given final step
  where
    curryNamed = curryNamedRL (RLProxy :: RLProxy remainingRL)

class
  ( Union remaining given expected
  , RowToList remaining remainingRL
  ) <= CurryNamedRL
  (remainingRL :: RowList)
  (expected    ::  # Type)
  (remaining   ::  # Type)
  (given       ::  # Type)
  (final       ::    Type)
  (step        ::    Type)
  | remainingRL expected given final -> step remaining
  where
    curryNamedRL :: RLproxy remainingRL ->
      (Record expected -> final) ->
      (Record given -> step)

instance curryNamedNil ::
  TypeEquals expected given =>
  CurryNamedRL Nil expected () given final final
  where
    curryNamedRL _ = compose from

instance curryNamedCons ::
  ( ListToRow remainingRL remaining
  , Union remaining given expected
  , CurryNamed expected remaining final step
  ) => CurryNamedRL (Cons sym t rl) expected remaining given final step
  where
    curryNamedRL _ f g = curryNamed (f <<< flip unsafeMerge g)

Show, Eq instances for Record type

If I understand right, using RowList its now possible to define Show and Eq instance for Record type. Is there any plans to add these instances? Without Show instance for Record types its hard to use the repl. (from)

Builder.merge could be "safer"

At this moment this program is compiled with any issues

module Main where

import Data.Record.Builder as Builder

union :: forall r3 r1 r2. Union r1 r2 r3 => { | r1 } -> { | r2 } -> { | r3 }
union r1 r2 = Builder.build (Builder.merge r2) r1

y :: {a :: Int, a :: String }
y = {a: 1 } `union` {a: "foo" }

z :: {a :: Int, a :: Int }
z = {a: 1 } `union` {a: 1 }

Which I guess should be rejected.

changing type of union fixes the issue

import Type.Row (class RowListNub, class RowToList)

union
 :: forall r1 r2 r3 r3l
  . Union r1 r2 r3
 => RowToList r3 r3l
 => RowListNub r3l r3l
 => { | r1 }
 -> { | r2 }
 -> { | r3 }
union r1 r2 = Builder.build (Builder.merge r2) r1

I think adding similar constraints to Builder.merge will make it much safer.

If we add such constraints error is not that informative, this is what you would get for:

{a: 1 } `union` {a: "foo" }

error: Could not match type Nil with type Cons "a" String Nil

With special class (for example SafeMerge) + Fail constraint the error could be made more useful.

/cc @MonoidMusician @justinwoo

Record kind as type parameter? Bug?

The following works:

generate :: forall r. { | r} -> { x :: Int | r }
generate = ?a

But this doesn't:

data E (a :: # Type) = E
generate :: forall r. E { | r} -> E { x :: Int | r }
generate = ?a
Could not match kind

     # Type

   with kind

     Type

What am I doing wrong? It seems like the kind of the parameter of E is ignored and that the forall r is defaulting to Type instead of # Type. However,

generate :: forall (r :: # Type). E { | r} -> E { x :: Int | r }
 Mai...    59  20 error    Err...   Unable to parse module:
   unexpected (
   expecting identifier

kind signatures in a forall don't seem to be supported. What do I do?

Bug: `union` causes type confusion

Using "purescript": "^0.13.6" and package set psc-0.13.6-20200423

Here's my foot gun:

main :: Effect Unit
main = do
    Console.log "1"
    let x = union {y: 5} {y: "5"}
    Console.log "2"
    let s = show x
    Console.log "3"
    Console.log s

Causes TypeError: s.replace is not a function after logging 2.

Changing to let x = union {y: "5"} {y: 5} gets rid of error and produces expected representation: { y: "5", y: 5 }

Alternative `build` for building a record from scratch

Hi everyone! ๐Ÿ‘‹

Would anyone be opposed to a build' :: Builder {} { | row } -> { | row } function to build a record from scratch? The implementation is only flip build $ {} but itโ€™s a convenient little function to compose in some pointfree scenarios.

insert and forall in record

If i have forall in record then you can't insert

module Main where

import Prelude

import Control.Monad.Eff (Eff)
import Data.Record (insert)
import Data.Symbol (SProxy(..))
import Type.Row (class RowLacks)

type ConfigR r = (baz :: forall a. Array a | r)
type Config r = Record (ConfigR r)

add
  :: forall r
  . RowLacks "fiz" (ConfigR r)
  => Config r
  -> Config ( fiz :: String | r)
add c = insert (SProxy :: SProxy "fiz") "foo" c

error is:

  No type class instance was found for

    Prim.Union ( baz :: forall a. Array a
               | r4
               )
               ( fiz :: Entry
               )
               ( fiz :: t5
               | t6
               )

Any explanation of why it happens?

I guess if you have forall in record compiler can't calculate Union for it

Data.Record.Builder.modify

Asked on slack, but figure it is less likely to get lost here.

I noticed there's no modify for Data.Record.Builder (akin to modify in Data.Record). Was that an intentional choice or an oversight? If an oversight, I'll submit a PR.

It might also be possible if there were a Functor instance for Builder. Would this work without breaking things? I remember we talked about adding a Profunctor instance, but it would break something. So, Functor is the next best thing, I think.

Create a partially applied record constructor

Consider this:

F <$> x <*> y <*> z

This has a problem traditionally that you need to make sure you get the x/y/z order correct. If you're making forms (web forms) with e.g. a formlet library, this becomes pretty hard to manage.

What if we could do this?

partially F <$> pure {x:a} <*> pure {y:b} <*> pure {z:a}

(or alternatively by using SProxy :: SProxy "a", etc.)

So that partially F would produce a function of one argument of one of the x, y or z fields (or more, I guess). When provided with one, it accepts another argument until all fields have been consumed.

I made special version of <*> that achieves something similar:

module Record.Apply where

import Control.Applicative
import Prim.Row (class Nub, class Union)
import Record (disjointUnion)

-- API

applyFields
  :: forall f inner outer combined.
     Union inner outer combined
  => Nub combined combined
  => Apply f
  => f { | inner }
  -> f { | outer }
  -> f { | combined }
applyFields getInner getOuter =
  disjointUnion <$> getInner <*> getOuter

infixl 5 applyFields as <|*>

-- Demo

newtype Foo = Foo { x :: Int, y :: String, z :: Array Int }

demo :: forall f. Applicative f => f Foo
demo = Foo <$> pure {y: ""} <|*> pure {x: 2} <|*> pure {z: []}

Which works nicely, but I think re-using <*> would be cool too. Any ideas?

Rename field?

It seems like a silly thing, but I have a need for this with simple-json usage. Should we add one to this library?

So far my implementation is as follows (from here):

rename :: forall prev next ty input inter output
   . IsSymbol prev
  => IsSymbol next
  => RowCons prev ty inter input
  => RowLacks prev inter
  => RowCons next ty inter output
  => RowLacks next inter
  => SProxy prev
  -> SProxy next
  -> Record input
  -> Record output
rename prev next record =
  insert next value inter
  where
    value = get prev record
    inter :: Record inter
    inter = delete prev record

Add `mergeFlipped`

Add mergeFlipped and an infix operator //

As a user I'm typically more thinking in terms of mergeFlipped than merge, as I want to overwrite a record with some new fields. E.g. in Typescript I can do

const newRecord = { ...otherRecord, more: 23 }

and in dhall // is avaiable as a mergeFlipped operator:

{ home       = "/home/bill"
, privateKey = "/home/bill/.ssh/id_ed25519"
, publicKey  = "/home/blil/.ssh/id_ed25519.pub"
} // { home = "/home/john" }

So in Purescript I would also like to do the following and I think it makes for a nice addition to purescript-record:

person :: { age :: Int
, firstName :: Maybe String
, professional :: String
}
person = 
  { firstName: "John", professional: true } //
  { professional: "Software Engineer", age: 58 } // 
  { firstName: Just "Mike" }

I have created a PR to illustrate this #83

Add an `unsafeHas` function

I know this is a somewhat dubious request :P but I'd like an unsafeHas function. I would like to add something to purescript-variant for partial matching with a record (right now it must always be total). It's possible for me to do this by contracting the variant first, which essentially does the "has" check for me, but it requires me to build a List of IsSymbol constraints on every invocation, and then walk the list to see if the variant tag is within bounds. However, if I have a record of case eliminators, then I could just use an unsafeHas function to do the dynamic lookup instead, which wouldn't require any IsSymbol constraints.

simple function doesn't typecheck

Can someone tell me why this simple function

singleton :: forall s a r. IsSymbol s => RowCons s a () r => SProxy s -> a -> Record r
singleton s a = insert s a {}

produces the following error, which seems like it's coming from the RowLacking constraint that should be trivially satisfied

  No type class instance was found for

    Prim.RowCons s4
                 Entry
                 ()
                 t5

  The instance head contains unknown type variables. Consider adding a type annotation.

while applying a function insert
  of type IsSymbol t0 => RowLacks t0 t1 => RowCons t0 t2 t1 t3 => SProxy t0 -> t2 -> { | t1 } -> { | t3 }
  to argument s
while inferring the type of insert s
in value declaration singleton

where s4 is a rigid type variable
      t2 is an unknown type
      t0 is an unknown type
      t3 is an unknown type
      t1 is an unknown type
      t5 is an unknown type

drop fields

It would be nice to have a function that will drop/delete unnecessary record fields.

drop :: forall r1 r2. Record r1 -> Record r1 where r2 is a subset of r1

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.