mstade / funkis Goto Github PK
View Code? Open in Web Editor NEWFunctional JavaScript
License: MIT License
Functional JavaScript
License: MIT License
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.
A good talk on a few different kinds of data structures.
http://www.infoq.com/presentations/Functional-Data-Structures-in-Scala
Will add more to this issue as I go.
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.
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.
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.
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.
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.
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.
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.
Just dropping this here 'cause I keep forgetting what it's called:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.