leopard icon indicating copy to clipboard operation
leopard copied to clipboard

Should the library, or the compiler, absorb Scratch's unusual behaviours?

Open joker314 opened this issue 7 years ago • 5 comments

Scratch has some unexpected behaviours designed to make anything the programmer throws at it work.

Let's take the 'set costume' block as an example. Here's your implementation:

https://github.com/PullJosh/scratch-js/blob/d415d1d74b0b4516bd827a87499f7b1306d595f8/scratch-js/Sprite.js#L11-L19

This would be the intuitive way of doing it. However, Scratch's algorithm is different:

  • number takes priority
    • NaN, Infinity, -Infinity are the first costume
  • string takes priority if a costume with that name exists
  • 'next backdrop' and 'next costume' special arguments are checked next
  • Treat strings/booleans/etc as numbers, but pure whitespace does not count as a number
  • Default to doing nothing

(Note that backdrops have a slightly different behaviour in 2.0, but in 3.0 there will be a breaking change to make them consistent with costumes, so we are allowed to not worry about this)

Who's responsibility is it to deal with all this unusual behaviour? Booleans have been used as costume names; so I don't think it's okay to ignore these intricacies.

In this case, because you can drop blocks in the slot, a compiler can't always know what the requested costume will be at runtime. However, it can do some stuff: for instance, it can know if a boolean will be returned by inspecting the block shape... except if you store a boolean in a variable/list (and note: type is preserved in these things, no matter how much Scratch tries to hide it from the programmer)

My conclusion, then, is that it has to be the library which replicates these quirks, because otherwise a compiler would have to add a bit of boilerplate each time, just in case... this would end up creating unintuitive JavaScript if the boilerplate has nothing to do with the code you're running (e.g. checking for a boolean when that could never happen).

So, if we want to be able to compile Scratch to JS, we will need to embed these quirks into our library (including the one I just referenced). Do you agree?

joker314 avatar Oct 21 '18 16:10 joker314

My conclusion, then, is that it has to be the library which replicates these quirks, because otherwise a compiler would have to add a bit of boilerplate each time, just in case...

Agreed. If quirks are to be handled, it needs to happen in the library, not the compiled code.

If we want to be able to compile Scratch to JS, we will need to embed these quirks. Do you agree?

Yes. It makes more sense to focus on Scratch compatibility than on making reasonable design choices. :stuck_out_tongue:

Where possible, I'd also like to include both options (Scratch mode and smart mode). For example, the "fast" mode in the touching block doesn't interfere with Scratch behavior at all, but it's a nice feature to have for people who want to build on their games directly in JS.

Another way to handle this is to have project-wide options available, where users can turn on/off Scratch quirks like preventing sprites from going off the stage. When the compiler gets a GUI, these types of settings can each have their own checkbox, and eventually be passed as options to Project.


With all that in mind, I'm going to start by focusing on normal use cases, and deal with quirks later. Right now the goal is breadth of features, not depth of implementation details. Later it will make sense to go back and dig into quirks and details. (Although if anyone wants to submit issues for all the Scratch quirks I'm missing right now, that would be greatly appreciated.)

PullJosh avatar Oct 21 '18 16:10 PullJosh

I'd lean for having this library be small and compact, focusing on decent design decisions rather than replicating weird Scratch behaviour.

We can always make a compatability wrapper (polyfill!) to implement 2.0-ish weird behaviour. This can be separate to keep sole-JS builds smaller. Likewise, we can have a 3.0 compatability library and a 2.0 compatability as there's breaking changes between the two.

bates64 avatar Oct 24 '18 11:10 bates64

We can always make a compatability wrapper (polyfill!) to implement 2.0-ish weird behaviour.

This sounds great in theory, but I'm curious how it would look from the user's end in practice. How would one enable/disable the compatibility libraries?

Also, how extreme do you want to take this? Should the direction system default to the regular JS one (radians, counter-clockwise) until a compatibility library is attached? Should it be possible to toggle on/off the features of each library?

PullJosh avatar Oct 24 '18 16:10 PullJosh

const scratch = require('scratch-js/compat3'), then use the library as normal. It'd be a wrapper.

bates64 avatar Oct 24 '18 17:10 bates64

Ah. Neat!

PullJosh avatar Oct 25 '18 02:10 PullJosh