-
Notifications
You must be signed in to change notification settings - Fork 149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How does Mori deal with functors, types, and interfaces in JS? #181
Comments
Just a passing enthusiast, but here are my thoughts. There's no real concept of a native functor interface in Clojure's because map is defined as a standalone function rather than as a protocol. There's no way for a new type to implement a functor interface. Instead, map attempts to create a seq from your collection and then iterates over it, applying your function and always returns a mapped list. A true functor would always return a collection of the type it was operating on. From the
So, anything that can be seq'd can also be mapped. The closest thing we can get to a functor interface is to implement Because map is a completely separate entity from list, it wouldn't make much sense to try and express it in terms of Javascript's prototypal inheritance either. // this style of code is a good fit for prototypes
list(1, 2, 3).map(f)
// this style is bad fit for prototypes
map(f, list(1, 2, 3)) You would have to do some serious voodoo to get map onto a prototype of list for Mori and you'd be going against the semantics of the Clojure. In Mori's case, the ClojureScript compiler simply compiles the native versions of map and list and the same check happens in Javascript code instead. All this said, there's no reason you couldn't implement your own functor protocol (in fact someone already has) which I think (in theory) could be compiled by the ClojureScript compiler. I'm not exactly sure how the compiler handles protocols, but I doubt it's with prototypes. I'm fairly new and still learning a lot about the language too. These are mostly just my guesses after a few months of playing with both. If there are any blunders in, please point them out and put me on the right track. |
My real motivation is evident in your example here: // this style of code is a good fit for prototypes
list(1, 2, 3).map(f)
// this style is bad fit for prototypes
map(f, list(1, 2, 3)) It seems odd that map needs to be specific to your list. Map is like map(f, MyList(1, 2, 3))
map(f, Set(1, 2, 3))
map(f, OrderedSet(1, 2, 3))
map(f, HashMap(1, 2, 3)) It seems to me like you should be able to use the same map function for mapping over anything thats mappable.. |
And that's the way it already is in Clojure and Mori. The example was there to show that only the first of the two styles is appropriate for expressing with prototypes. Because Clojure's functions are kept separate from the data structures, the second example is the way you'd use them in JS and it doesn't fit with the model of prototypal inheritance. |
Interesting. So if I wanted to create my own functor in Clojure, how would I specify the fact that it is mappable, so that the generic |
@ccorcos: The short answer is to implement the Iterable protocol from ES2015. The long answer follows. Actually, Clojure has a feature called protocols, which are a lot like interfaces. Many of the functions in Mori (including Try applying > mori.map(x => x, 3)
Error: 3 is not ISeqable ClojureScript (and thus Mori) looks up protocol implementations via properties that are added onto collections. If you load up a development build of Mori, you can inspect values to see this: > Object.keys(vector)
[ 'meta',
'cnt',
'shift',
'root',
'tail',
'__hash',
'cljs$lang$protocol_mask$partition0$',
'cljs$lang$protocol_mask$partition1$' ] I would bet that if the right properties were added to a prototype, then values of the corresponding class could be used as But the production build of Mori has been run through aggressive optimizations via Google Closure Compiler, which rewrites property names. So if you try inspecting values created from the production build, this is what you see: > Object.keys(vector)
[ 'k', 'g', 'shift', 'root', 'W', 'p', 'j', 'q' ] There is another pull request on Mori, #108, that adds a feature for registering plain JavaScript values as implementations of ClojureScript protocols. But if you just want your types to be usable as It happens that for compatibility with JavaScript, Mori accepts Iterable values in place of It happens that I wrote Flow type definitions for Mori that provide a clear picture of which functions take |
@hallettj Awesome! thanks for the explanation. Thats makes more sense. It looks like you have the please of living in your own world in Clojurescript. I'm trying to make due in JS land. Looks like I'll be resorting to using some sort of prototype methods though... |
After further experimentation, I think that I may have been wrong about functions that take |
The problem with Iterables is they're mutable... I'm not sure Clojure would like that... |
Javascript arrays are also mutable, but Mori functions consume them without a problem. My point is that converting a mutable iterable into an immutable |
When Mori builds a list, how does it define a functor interface saying that the list has a map? And more specifically, how is this transpiled into JS? does it use .prototype methods?
The text was updated successfully, but these errors were encountered: