Giter Site home page Giter Site logo

Comments (9)

danprince avatar danprince commented on July 19, 2024

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 seq docs:

Returns a seq on the collection. If the collection is empty, returns nil. (seq nil) returns nil. seq also works on Strings, native Java arrays (of reference types) and any objects that implement Iterable. Note that seqs cache values, thus seq should not be used on any Iterable whose iterator repeatedly returns the same mutable object.

So, anything that can be seq'd can also be mapped. The closest thing we can get to a functor interface is to implement Iterable, which allows our data to be seq'd. For instance, list is an instance of clojure.lang.PersistentList, which implements java.util.List, which implements java.lang.Iterable.

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.

from mori.

ccorcos avatar ccorcos commented on July 19, 2024

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 +, it a general concept. So you should be able to use it to swap out your own date structures...

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..

from mori.

danprince avatar danprince commented on July 19, 2024

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.

from mori.

ccorcos avatar ccorcos commented on July 19, 2024

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 map function knows that to do with it?

from mori.

hallettj avatar hallettj commented on July 19, 2024

@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 map) take arguments that implement the ISeqable protocol.

Try applying map to a value that cannot be mapped, and see what happens:

> 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 ISeqable values.

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 ISeqable values, then there is an easier way.

It happens that for compatibility with JavaScript, Mori accepts Iterable values in place of ISeqable. So if you put an @@iterator method in your prototype, or individually in your objects, you are set.

It happens that I wrote Flow type definitions for Mori that provide a clear picture of which functions take Seqable (a.k.a. ISeqable) arguments vs more specific argument types.

from mori.

ccorcos avatar ccorcos commented on July 19, 2024

@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...

from mori.

hallettj avatar hallettj commented on July 19, 2024

After further experimentation, I think that I may have been wrong about functions that take ISeqable values also taking ES2016 Iterable values. Anywhere an ISeqable value is expected, you can use a plain javascript array or a javascript string (which is treated as a list of characters). But it looks like that support for non-Clojure values does not generalize to other Iterable types. For example, collections from Immutable do not work, even though they are iterable. It would be nice to update Mori to treat arbitrary iterable values as ISeqable.

from mori.

ccorcos avatar ccorcos commented on July 19, 2024

The problem with Iterables is they're mutable... I'm not sure Clojure would like that...

from mori.

hallettj avatar hallettj commented on July 19, 2024

Javascript arrays are also mutable, but Mori functions consume them without a problem. My point is that converting a mutable iterable into an immutable ISeq is just as easy as converting a mutable array into an ISeq - but working with any iterable input is a more general solution.

from mori.

Related Issues (20)

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.