Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Jun 19 22:57
    Avaq commented #151
  • Jun 19 22:27
    Avaq review_requested #151
  • Jun 19 22:27
    Avaq ready_for_review #151
  • Jun 19 22:27
    Avaq synchronize #151
  • Jun 19 22:27

    Avaq on performance

    Add tests to assert explicitly … Improve performance of method-d… Improve performance of TypeClas… and 1 more (compare)

  • Jun 19 21:24
    Avaq synchronize #151
  • Jun 19 21:24

    Avaq on performance

    Remove unnecessary function wra… (compare)

  • Jun 19 21:18
    Avaq synchronize #151
  • Jun 19 21:18

    Avaq on performance

    Improve performance of method-d… Improve performance of TypeClas… (compare)

  • Jun 19 21:10
    Avaq synchronize #151
  • Jun 19 21:10

    Avaq on performance

    Add a baseline benchmark This … Add a few more benchmarks to th… Improve performance of method-d… and 1 more (compare)

  • Jun 19 17:23
    Avaq commented #2
  • Jun 19 17:13
    masaeedu commented #2
  • Jun 19 16:27

    davidchambers on lenses

    work in progress (compare)

  • Jun 19 16:24

    davidchambers on ci

    (compare)

  • Jun 19 16:22

    davidchambers on ci

    (compare)

  • Jun 19 16:08
    andreev-danila starred sanctuary-js/sanctuary
  • Jun 19 15:36

    Avaq on structural-equality

    (compare)

  • Jun 19 15:36

    Avaq on master

    Update the equals function to f… Add a test to assert that equal… Merge pull request #154 from sa… (compare)

  • Jun 19 15:36
    Avaq closed #154
silly-goat
@silly-goat
"object"
dotnetCarpenter
@dotnetCarpenter
yeah...
David Chambers
@davidchambers
:point_up: This is why we need both typeof process === 'object' and process != null.
dotnetCarpenter
@dotnetCarpenter
I don't know how we could test this on scrimba.com without publishing. Their editor does not seem to be open source but they do have a presence at github: https://github.com/scrimba/community
And a Discord server: https://discord.gg/Bs6SAv7
They are based in Oslo so they on the same time as me. Now it's almost 23, so I doubt they will be there.
David Chambers
@davidchambers

But what about other environments that has a util module?

This is a good point. The util module is only used to access util.inspect.custom in order to customize the representations of def-defined functions in the REPL. This is not core functionality.

dotnetCarpenter
@dotnetCarpenter
They have forked unpkg so I guess they use that. But still, that requires publishing a pre-release package AFAIK.
The util module is only used to access util.inspect.custom in order to customize the representations of def-defined functions in the REPL.
Hmm.. could it be easily polyfilled instead of using the entire util module?
Didn't node.js began to use Symbols for that? I'll have to search a bit
David Chambers
@davidchambers
Yes, it's a symbol:
> typeof util.inspect.custom
'symbol'
dotnetCarpenter
@dotnetCarpenter
So Symbol.for('nodejs.util.inspect.custom') === util.inspect.custom
David Chambers
@davidchambers
Oh, I see where you're going now!
dotnetCarpenter
@dotnetCarpenter
yes? We don't need to require ('util')
David Chambers
@davidchambers
Clever!
dotnetCarpenter
@dotnetCarpenter
Don't know about Deno though
I think you are better suited to make the appropriate changes from util.inspect.custom to Symbol.for('nodejs.util.inspect.custom'), since I don't even know where it is used.
David Chambers
@davidchambers

We could use Symbol.for('nodejs.util.inspect.custom') in every environment, but I don't like this (emphasis mine):

The Symbol.for(key) method searches for existing symbols in a runtime-wide symbol registry with the given key and returns it if found. Otherwise a new symbol gets created in the global symbol registry with this key.

dotnetCarpenter
@dotnetCarpenter
no.. but what would be the harm? An extra entry in the global symbol registry? Wrong representation in string output in none node.js environments? Unfortunately Symbol.keyFor requires a Symbol, which brings us back to square one. You can only test if Symbol.for('nodejs.util.inspect.custom') is correct by having access to util.inspect.custom.
David Chambers
@davidchambers

but what would be the harm? An extra entry in the global symbol registry?

Yes, this. I don't like the idea of importing the module having a side effect, however inconsequential it may seem.

dotnetCarpenter
@dotnetCarpenter
I guess there is no harm. Assigning the wrong (outside node.js)Symbol.for('nodejs.util.inspect.custom') as a property to an object should be the same as assigning util = {inspect: {}}
Ahh ok. Yes that is true, there will be a single extraneous entry in the global symbol registry named 'nodejs.util.inspect.custom'.
So back to environment detection?
typeof process === 'object' && process != null && process.versions != null && process.versions.node != null
David Chambers
@davidchambers
I think it all works…

Current approach:

  var inspect = typeof util.inspect.custom === 'symbol' ?
                util.inspect.custom :
                /* istanbul ignore next */ 'inspect';
    wrapped[inspect] = wrapped.toString = always0 (typeSignature (typeInfo));

Alternative approach:

    wrapped.toString = always0 (typeSignature (typeInfo));

    // Node.js
    if (
      typeof process === 'object' &&
      process != null &&
      process.versions != null &&
      process.versions.node != null
    ) wrapped[Symbol.for('nodejs.util.inspect.custom')] = wrapped.toString;

    // Deno
    if (
      typeof Deno !== 'undefined' &&
      Deno != null &&
      typeof Deno.customInspect === 'symbol'
    ) wrapped[Deno.customInspect] = wrapped.toString;
How does this look to you, @dotnetCarpenter?
We would no longer reference util anywhere. :)
dotnetCarpenter
@dotnetCarpenter
Let's get rid of util! Looks a lot better. I like that this also means that errors (like require did) will happen where the code is needed. Not saying that your example could possible blow up.
I realise that this means changing a lot of repos.. Do you want a helping hand with some of them?
David Chambers
@davidchambers
I'm happy to make the changes, but I would certainly appreciating you confirming that the changes fix your Scrimba issue once we have released new versions of the various packages. :)
dotnetCarpenter
@dotnetCarpenter
Sure thing! :thumbsup:
Perhaps you want to make pre-releases, that I can test?
Maybe just pre-release sanctuary-def and I will check on Scrimba
David Chambers
@davidchambers
I could do. I will probably just release X.0.0 and release X.0.1 if we need to tweak something.
dotnetCarpenter
@dotnetCarpenter
sounds good. But still you might want to start with sanctuary-def and if that works, you can continue with the rest.
David Chambers
@davidchambers
Good idea.
dotnetCarpenter
@dotnetCarpenter
I'll be online later, so you can shout here and I will test.
dotnetCarpenter
@dotnetCarpenter
@jceb:matrix.org Thanks. That's a lot to read through but looks like it's worth it. :)
dotnetCarpenter
@dotnetCarpenter
And thanks for starting the cheat sheet. It looks promising and I starred it for future reference
dotnetCarpenter
@dotnetCarpenter

@davidchambers This is a continuation of https://gitter.im/sanctuary-js/sanctuary?at=60c62e2dd20143617e424437 and https://gitter.im/sanctuary-js/sanctuary?at=60c63e19d855766185d6112c.

I need to create Date objects of my custom data type. Right now, I'm wondering what would be the best implementation. It's rather simple really. I got a $.RecordType:

{
    endDate: $.NonEmpty ($DateIso),
    startDate: $.NonEmpty ($DateIso),
}

where $DateIso is:

const $DateIso = $.NullaryType
  ('DateIso')
  ('https://www.firefund.net/')
  ([$.String])
  (x => /^\d{4}-\d{2}-\d{2}$/.test (x))

When ever I receive an data object for the database, I test the data with:

S.is (recordType) (x)

Where recordType is the previous mentioned $.RecordType.

The entire flow is super nice and simple as:

// createNewProject :: Object -> Future a b
const createNewProject = S.pipe ([data => Table.insert (data), run, execute])

// validateNewProject :: Object -> Future a b
const validateNewProject = S.pipe ([
  S.tagBy (schema.validate),
  S.either (reject) (createNewProject),
])

The problem with the solution above is that the database needs Date objects and not a date string (which I will get from a JSON feed) e.g. "2021-04-30".

My question is, if there is a clever way to use sanctuary-def to convert the string to Date or if I should convert the two fields explicitly in the function that calls validateNewProject? The former would be a generalization and the latter would have to be a concrete implementation to that specific data object.

dotnetCarpenter
@dotnetCarpenter
If there is no conversion functionality in sanctuary-def, I can always do:
// validateCampaign :: Object a -> Either a a
const validateCampaign = S.pipe ([
  S.tagBy (schema.validate),
  S.map (campaign => {
    campaign.endDate = new Date (campaign.endDate)
    campaign.startDate = new Date (campaign.startDate)

    return campaign
  }),
])

// insertCampaign :: Object -> Future a b
const insertCampaign = S.pipe ([data => Table.insert (data), run, execute])

// createNewCampaign :: Object -> Future a b
const createNewCampaign = S.pipe ([
  validateCampaign,
  // @ts-ignore
  S.either (reject) (insertCampaign),
])
dotnetCarpenter
@dotnetCarpenter
sorry for renaming the functions but I realised that I'm not dealing with projects but campaigns.
David Chambers
@davidchambers
This looks good to me, @dotnetCarpenter, aside from the mutation in the arrow function you are providing to S.map. sanctuary-def is not in the business of transforming data, so you were right to define the transformation function yourself. :)
dotnetCarpenter
@dotnetCarpenter

Thanks for the feedback @davidchambers ! Much appreciated. While I understand the benefits of immutable data I find it difficult to justify all the time.

// validateCampaign :: Object a -> Either a a
const validateCampaign = S.tagBy (schema.validate)

// insertCampaign :: Object -> Future a b
const insertCampaign = S.pipe ([
  campaign => {
    campaign.endDate = new Date (campaign.endDate)
    campaign.startDate = new Date (campaign.startDate)

    return campaign
  },
  data => Table.insert (data),
  run,
  execute
])

// createNewCampaign :: Object -> Future a b
const createNewCampaign = S.pipe ([
  validateCampaign,
  S.either (reject) (insertCampaign),
])

Above insertCampaign mutates the data to be inserted. So the issue here would be if the callee of createNewCampaign expect endDate and startDate to be String and not Date, right? Or am I missing the point?

Should I clone the object before updating the two fields?
David Chambers
@davidchambers

In general it's a good idea to define as few impure functions as possible.

I suggest this approach:

const insertCampaign = S.pipe ([
  campaign => ({
    ...campaign,
    startDate: new Date (campaign.startDate),
    endDate: new Date (campaign.endDate),
  }),
  Table.insert,
  run,
  execute,
]);

If for some reason it's necessary to mutate the function's argument, I suggest renaming the function something like UNSAFE_insertCampaign or mutateThenInsertCampaign.

dotnetCarpenter
@dotnetCarpenter
You are probably right. I have no experience building large application in FP. And in OOP I use methods on a object/class so I expect it to own and mutate properties at will.
David Chambers
@davidchambers
const y = f (x);
console.log ('x:', x, 'y:', y);
:point_up: I have spent time debugging code like this only to discover that the x I am logging is not the x I provided to f. :scream:
dotnetCarpenter
@dotnetCarpenter

I see. Yeah, that would be difficult to track down.

In OOP you would have something like below:

const y = new Y
y.f (x) // <- mutate and own x
console.log (y.getX ()) <- you should only get x from an accessor, since y now owns x.

But I have been down that road and somehow, even when trying to abide SOLID principles, a change to a business rule means changing a lot of class methods. And after a few times, it all feels like cruft and a re-write seems like a better and better idea.