Git Product home page Git Product logo

funkis's People

Contributors

mstade avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

funkis's Issues

Consider packages

This stuff is growing out of control. I wonder whether it'd make sense to package things up just so lib isn't just one long list of functions. Clojure has core and stuff like that, maybe just bundling stuff into folders would work, and then you require things by doing something like `require('funkis/core/thing')? Or perhaps even going so far as to moving stuff into separate projects altogether, so there's a funkis.core, funkis.stuff, etc. But then also comes maintenance headaches of multiple repos etc.

Obviously little more than lipstick on a pig, but worth thinking about. Later. Maybe.

Is additive mutability dangerous?

This library presumes that mutability is in most cases bad and should be avoided. Copy-on-write or sharing should be used, along with new instances that represent subsequent states. An example:

const foo = Object.freeze([1, 2, 3])
const bar = Object.freeze(foo.concat([5, 6, 7]))

In this example, both foo and bar are made immutable, and so the only way for bar to be defined is by virtue of copying foo into a new array. In this example, we're never mutating foo, but we are mutating the environment – twice. First, foo is declared, then of course, bar. However, this is additive, meaning foo and bar didn't exist at all before their declaration. As such, one couldn't really reason about them at that point either (other than possibly asserting their non-existance.)

So my thinking here is, is additive mutability on object – not just the environment – similarly "safe", as it were? If foo wasn't sealed, but properties could be added just not changed or removed once set, would that make foo more difficult to reason about?

In a way, yes, because it does mutate the internal state of foo. If you add a property like so:

const foo = [1, 2, 3]
foo[3] = 4

... it won't just affect the new property, but also change the internal state of foo such that it's length property updates its value. As well, if there's code to reflect on foo to enumerate all its properties it would unfortunately return even the new property. This does change the state of the world and as such is tricky to manage.

However, what about less obvious properties, such as "internal" properties? An example is adding metadata to an object. The safe and strictly immutable way of doing this is to create a new object, add the metadata as a property of some kind, and make sure to copy over all the fields of the old object (copy-on-write semantics) or make sure the new object has the old object as a prototype (structural sharing.) Everytime you change a property, the same thing has to happen.

In especially copy-on-write scenarios, but possibly also with structural sharing (via prototypes at least) this may have some non-trivial performance concerns. So my question is this: are there cases of additive mutability which are not dangerous, and could be exploited if necessary? Needs some investigation.

Add debug utilities

It'd be nifty if there were some nice debugging utils for inspecting stacks and executions. Just recently I saw a talk by @brianarn where he showed a function that neatly wrapped other functions and logged their name, input, and output as they executed. Something like that could be very useful for interactive debugging in the repl.

Consider using eval for function composers

variadic and other functions return a wrapper, which has a different signature than the underlying function. This can get confusing in debugging, since it effectively hides the underlying in stacks and variable slots. It would be nice if these functions would be able to copy the signature where appropriate, or otherwise retain as much crucial information as possible – such as the name and argument list. This doesn't necessarily apply to all composers, but certainly to things such as variadic which really just changes the meaning of the last parameter.

A concern with this is that the use of eval is fraught with cargo culture induced FUD, and so is frowned upon by essentially everyone – including myself.

So I'm opening this issue as a means of clearing up some of the mystery surrounding this particularly useful construct, since it enables some pretty interesting meta programming capabilities. First, here's a performance test comparing the execution performance of a function wrapped by eval, and one wrapped by a regular function literal:

http://jsperf.com/eval-wrapping

Running this on Aurora, I get a virtually non-existant performance difference between the two, with eval coming out on top no less, suggesting that at least in this kind of context (which is equivalent to that of variadic, btw) performance is not a real concern.

Other things to explore is how this affects stack traces and debugging capabilities. My gut feeling, from having read some articles regarding this specific thing, is that debugging evaluated code is tricky, but that modern engines do a pretty bang up job. What's important to note though, is that the main context where eval would be used in funkis is to wrap other functions. This means that the code that is evaluated is largely insignificant, and if debuggers just step right through that code it's even better, since the wrapper code is pretty much useless debug data anyway.

Predicate Dispatch

In discussions on how to route URLs, the suggestion came up to have generic predicate dispatch functionality; the URL patterns would be the predicates in this case.

It's not immediately clear to me how this would work, but supposedly it means the predicate dispatch function just operates on a generic decision tree, and the building of that tree must be configurable since the order of priority, precedence and such really do depend on the pattern language. Switching on JS types is very different from switching on URI templates, for instance.

@sammyt -- please do chime in.

Change the return value of iterating over object properties with `seq`

Currently, seq will return objects when iterating over enumerable properties, where the only property on the returned object is the value of that iteration. For example:

props = seq({ foo: 'wibble', bar: 'bob' })

console.log(props.first) // { foo: 'wibble' }
console.log(props.rest.first) // { bar: 'bob' }

(NB: order isn't guaranteed, but this seems to be the result in node anyway.)

Now, this is all well and good, but there's still no easy way of getting the one and only property out of these objects without knowing the names in advance. As a solution, I'm thinking of adding two non-enumerable properties to these objects: name and value (or just 0 and 1 to make it array-like?)

That way, you could use props.first.name or props.first.value to get the name/value respectively without having to know anything upfront:

props = seq({ foo: 'wibble', bar: 'bob' })

console.log(props.first.name, props.first.value) // foo wibble
console.log(props.rest.first.name, props.rest.first.value) // bar bob

Since these would be non-enumerable properties, the key/value pair objects returned by the seq should still be usable in object merging scenarios since those usually only work on own properties and not prototypes or non-enumerable properties.

Make all functions curried by default

If all functions are by default curried, then in many cases you can do partials without needing partial. Example:

exists = not(partial(eq, null))

... becomes ...

exists = not(eq(null))

Since eq needs at least two (well, in the current impl it only ever cares about two) arguments it would return a function rather than a result. That is, provided all functions are curried. There's an obvious performance hit to this, but I reckon it could be worth it for the lovely code it could produce.

Track performance

This project could benefit from a suite of performance tests. Not sure what the best approach to this is; perhaps a good start would be to proxy unit tests to introduce instrumentation of tested functions. Not sure whether this is relevant or whether it's just pointless micro-benchmarking.

Some good performance tips can be found in this screencast: https://vimeo.com/68085383

Particularly the notes on binding, call/apply, and inspecting functions looking for this to see whether binding is necessary. That last point is very relevant for #11.

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.