
Skip – A programming language to skip the things you have already computed - pbowyer
http://skiplang.com/
======
qwerty456127
Just introduce a syntax to define immutable variables (e.g. like "val" in
Scala) and to mark particular functions as pure (easy to implement manually as
a decorator in Python, I've been using it a lot) and memoization becomes a
seemingly easy task. Why a new language?

By the way it seems very sad to me that the majority of imperative and hybrid
(functional×imperative) languages lack syntax for immutable variables: in just
so many cases I introduce a variable to store an intermediate result of an
operation an don't mean to re-assign it ever after, it's nice of a developer
to define this intention explicitly and of a compiler to raise an error when
the variable is re-assigned accidentally - this simple feature is among the
reasons why Scala programs usually are comparably easy to debug and run as
expected as soon as they get compiled successfully.

~~~
taneq
> and run as expected as soon as they get compiled successfully

If a non-trivial program runs as expected on the first try I get _really_
suspicious.

~~~
qwerty456127
So did I, the first Scala experience was shockingly pleasant (I indeed
"couldn't believe my eyes" and literally thought "that's so weird, it probably
works just so wrong that it results in an illusion of working right", it felt
like magic and this way frightening): it either fails at compile time
(throwing very informative error messages some of which can be unintuitive and
seem weird as long as you don't know what do they mean but let you identify a
problem easily as soon as you learn to read them) or just does what you meant
it to do. But the programs I've written can indeed be considered trivial - I
didn't make any serious use of actors or weird types, I mostly used it as "a
better C#" \+ humble amount of FP but it seems to me that this makes my point
make even more sense - write almost the same program in a very similar style
in 2 languages and one of them requires a way less debugging.

~~~
taneq
The hardest part of software development is figuring out exactly what you mean
it to do, though. Making a program that runs without crashing and does
_something_ is relatively easy.

------
thom
Incremental computation is a fascinating area of research, I've followed
Matthew Hammer's work on Adapton[1] for a while, as well as Daco Harkes work
on IceDust[2]. The issues are obviously quite deep (i.e. how do can you change
a single value and recalculate quicksort without the effects of the change
fanning out massively?) whether you're targeting existing languages and
runtimes or building from scratch.

I would very much like to see this as an area of active development both in
programming languages and distributed computing platforms. Current streaming
platforms are great and it's getting easier and easier to create declarative
data flow pipelines, doing real-time processing, splitting things into windows
etc. But on top of that I imagine incorporating incremental computation,
allowing you to keep a durable history of the event stream (or at least
manageable parts of it) in a way that automagically allows incremental
recalculation of upstream data when an underlying fact is retracted or
updated.

[1] [http://adapton.org/](http://adapton.org/)

[2] [https://dcharkes.github.io/](https://dcharkes.github.io/)

~~~
thom
Of course if that already exists then I'd love to hear about it! There seem to
be all sorts of hybrid stream/batch systems but none with quite this focus.
Effectively I want stuff that streams into a time series database in realtime,
with the opportunity to edit or delete events later. On top of that there
would be a processing pipeline with familiar streaming paradigms (transforming
data, windowing data arbitrarily, aggregating on top of that) with the
additional magic that the necessary events/windows/calculations re-fire if
some underlying data changes. All in a magically efficient and transparent
way.

~~~
frankmcsherry
Shameless plug for something that does all of these things (perhaps not
exactly as you want, but ..)

[https://github.com/frankmcsherry/differential-
dataflow](https://github.com/frankmcsherry/differential-dataflow)

~~~
thom
Wonderful! I feel bad that I didn't follow the work after hearing about Naiad
originally. Where might I go to read more about the fault tolerance story - I
note that's an ongoing area of research in the README. Also the link to the
Kafka adapter is dead, is that work ongoing or does the story end at the
capture/replay API? (Happy to bombard you with email if that's any more
manageable for you!)

Reading the 'when not to use Timely Dataflow' section on sorting, are there
any ideas from Nominal Adapton that are relevant in this distributed setting,
when we're talking about small updates or inserts?

~~~
frankmcsherry
Durability is a student's research project at the moment. It's up and running
for "Spark" style computations but the work has broader ambitions. In essence,
all of the internal state in DD are easily (automatically) serialized LSM
slabs, and it is cake to write them out and read them back (and almost cake to
make sense of them).

    
    
        https://eurosys2017.github.io/assets/data/posters/poster21-Lattuada.pdf
    

Which Kafka link is dead? The work has quiesced because any next step seems to
be involve assuming something about timestamps in the input. At the same time,
it seems to take about 10 lines of code to write a Kafka source or sink, minus
any careful worrying about acks for durability.

The DD and Adapton approaches may be a bit tricky to hybridize. Much
incremental compute works by moving through a _sequence_ of valid
configurations, and DD's main departure is generalizing this to partial
orders. So, maybe you could borrow ideas, but it would probably be original
research to do so.

Email is great, as it is def no longer about skiplang.

------
timdorr
> The Skip project concluded in 2018 and Skip is no longer under active
> development at Facebook.

Just a warning if you're interested in using this.

~~~
jverlaguet
I would like to add that I intend to maintain it and build a community around
it in outside of FB as it is mentioned on the front-page.

"The language, compiler and libraries are maintained as a side project by
Julien Verlaguet, the main designer of the language."

~~~
fusiongyro
How does your language compare to the glut of modern systems-ish languages
like Rust, Go, Swift, Julia, Nim? I see a lot of C++ influence here. Your docs
don't explain how you make parallelism ergonomic, but the "async" keyword is
getting popular. Why would I choose to invest in your language over one of
these other ones that has more momentum?

~~~
jverlaguet
Skip is not meant to be a system programming language. So the comparison with
Rust for example is difficult.

> "I see a lot of C++ influence" I don't see what gave you that impression.
> Perhaps the syntax? But I don't think it looks more like C++ than Java or
> C#.

> Why would I choose to invest in your language over one of these other ones
> that has more momentum?

There are several reasons, but of course I am biased: 1- builtin cache
invalidation 2- safe parallelism 3- predictable GC

While 2-3 can be found in other languages, I think I can argue that 1 is very
unique to skip.

The other thing is, what are you trying to build? If you what you want to do
is to develop an incremental tool, then Skip is probably the best option out
there right now.

Let's say you decided tomorrow to build a fully incremental C++ front-end, to
support auto-complete for example (I chose C++ because it is notoriously
complex). What would you write this in? I think Skip should be the language of
choice.

~~~
dgreensp
I can see why someone who stumbles on Skip would classify it as "systems-ish":
General-purpose statically-typed language that compiles to native code with an
emphasis on being fast with predictable performance. Go is thought of as a
systems language, and it is a garbage-collected language meant for writing
servers!

How would you describe when to use Skip? It looks really interesting.

~~~
the_duke
I don't know anyone who considers Go a systems language. That wording was also
removed from the Go website a long time ago.

The GC and the heavy weight runtime make it unsuitable in this space.

Skip seems very similar to Swift and Java, with inspiration from Rust wrt
mutability.

------
thedirt0115
Anyone have some insider knowledge on the history of this project? I'm curious
as to how a big multi-year create-a-language research project like this gets
pitched, accepted, and finally sunset -- Was it a full-time project? Were
there deadlines? Was this considered a success or a failure?

~~~
grawprog
The page itself mentions the project took place from 2015-2018. There's also
what looks like a devlog. I'm not sure how far back it goes but it may have
the details you're looking for.

------
X6S1x6Okd1st
> Skip also features a code-formatter to ensure consistent code style and a
> tool for running codemods.

Nice! I love this trend.

~~~
vjeux
Yeah, before joining the Skip team I worked on Prettier. Once you have an
automatic formatter, you can't really go back, so I ported the core algorithm
of prettier to Skip and built all the formatter for it.

My biggest surprise was that the language has been designed by people that
have been working on languages for their entire lives so it was dead simple to
write the formatter for it compared to JavaScript.

It only took me ~2 weeks to get it in a place where we could reasonably
convert the entire codebase to it. It took Prettier ~6 months to get to that
point.

~~~
mattdeboard
I recently read a couple github threads in prettier's repo that said prettier
won't do import optimization/organization, nor object property organization
(e.g. {c: 3, b: 2} -> {b: 2, c: 3}) because it has potential to cause AST
changes and could break things for people.

Do you think it's feasible to have a "jsfmt" tool or is the technical hurdle
too great?

~~~
vjeux
A lot of people have successfully coupled eslint with prettier, where they
enable eslint rules that do those transforms with autofix.

------
qwerty456127
By the way, I've got a question to programming language engineers and people
keeping track of emerging and experimental languages: is there a language
where everything (or almost everything, excluding elementary types and structs
perhaps) is an "actor" and every class method call is an asynchronous message
passing? Together with an idea of a heavily-memoized language (which Skip is
meant to be an implementation of) this idea won't leave my mind for years
since I've first learnt about the actor model.

~~~
hobofan
There was Axum[0], a research language by Microsoft.

[0]:
[https://en.wikipedia.org/wiki/Axum_(programming_language)](https://en.wikipedia.org/wiki/Axum_\(programming_language\))

~~~
qwerty456127
Unfortunately, it has been discontinued long ago almost immediately after
being introduced. And for the .Net ecosystem this means it can hardly be used
nowadays as it is probably incompatible with modern libraries.

~~~
hobofan
I used it for my senior project in high school (or the German equivalent of
that), to write a lattice gas cellular automaton. One day after deciding on my
topic, it was announced by Microsoft that they would discontinue it.

Even back then it was very buggy, and in a normal size program, you would
certainly run into some bugs. Sadly Microsoft has taken most resources
regarding Axum down some while after discontinuing it, so if someone would
want to try it out they would run into a lot of dead links.

Based on my fading memories (but take this with a grain of salt, since I was
much less experienced back then), I would say that Axum and it's actor-focused
model provided not enough upsides to warrant it being its own programming
languages. I had better experiences using libraries like Akka in JVM
languages, or Actix in Rust.

------
lifthrasiir
I always had my go-to questions for commercially sponsored programming
language projects after having done some of them: How did them start? Did them
pivot from, say, a personal project or were them thoroughly planned within the
company? How much resource was spent and how was the (current) fate of the
project determined?

------
chrisaycock
How are the side effects tracked? I couldn't find it in a brief read of the
source code. The documentation mentions _sequence points_ at which previous
side effects are guaranteed to have finished. But what actually tracks whether
IO or state changes have occurred?

~~~
tnowacki
IO is managed through very specific native extensions where updates to that IO
source are able to be tracked outside of the language and fed back in. For
example, we don't have anything that lets you read from command line or stdin.
But you can open and watch a file. Any changes to that file will be tracked by
the runtime.

Other non deterministic computation, like randomness or time is outright
banned in the tracked environment. (There is an opt-in 'untracked' modifier if
you want to write code outside of the reactive environment).

For other tricky behavior, like mutability/mutations on an object, the type
system tracks the mutability mode of any object. Only objects that are fully
immutable ('frozen' in the language) can be memoized inputs/outputs to a
function.

~~~
X6S1x6Okd1st
> For example, we don't have anything that lets you read from command line or
> stdin. But you can open and watch a file.

whats the difference between opening a file and watching it and watching
stdin?

~~~
TheDong
inotify and seek works on most files.

FIFO file descriptors, not so much.

If you can't seek backwards, you pretty much have to cache the whole contents
in memory, which seems wildly inefficient.

------
zellyn
Apparently this was a research project. Anyone have links to papers,
conclusions, results? Should be interesting…

~~~
vjeux
We posted about some of those in the blog over the years.
[http://skiplang.com/blog](http://skiplang.com/blog)

Some of the interesting ones:

\- An overview of how memoization works and the MVCC model behind the scenes:
[http://skiplang.com/blog/2017/01/04/how-memoization-
works.ht...](http://skiplang.com/blog/2017/01/04/how-memoization-works.html)

\- How pattern matching is implemented and the tricks to make goto work in
JavaScript: [http://skiplang.com/blog/2017/11/15/simulating-goto-in-
javas...](http://skiplang.com/blog/2017/11/15/simulating-goto-in-
javascript.html)

\- The work done on making error messages much more helpful by understanding
common idioms from other programming languages:
[http://skiplang.com/blog/2017/11/20/fixing-the-syntax-
barrie...](http://skiplang.com/blog/2017/11/20/fixing-the-syntax-barrier.html)

\- The macro syntax that elegantly solves a lot of use cases where dynamism is
commonly used
[http://skiplang.com/blog/2018/07/24/macros.html](http://skiplang.com/blog/2018/07/24/macros.html)

~~~
zackmorris
Thank you, I feel like Skip is very close to the language I've always wanted.
Its MVCC looks very similar to Clojure's, I'm having trouble finding a good
link about it:

[https://sw1nn.com/blog/2012/04/11/clojure-stm-what-why-
how/](https://sw1nn.com/blog/2012/04/11/clojure-stm-what-why-how/)

The gist of it is that instead of tracking multiple separate locations in
memory for values, the software transactional memory (STM) references data by
value. So if you assign the value {a: 42, b: 24} to two variables x and y,
that value is only stored in memory in one place that the variables both point
to. Then if one of the variables changes something, for example y.b = 25, this
big tree structure works like copy-on-write and makes copies of branches when
mutations occur. So internally, a: 42 is one reference and b: (24 or 25) is
another reference. So rather than using 4 cells of memory, we've only used 3.

This frees the developer from having to micromanage memory resources and makes
a lot of other things like concurrency "just work" without locks. Do I have
this correct?

~~~
Serow225
FWIW, MATLAB does a lot of this kind of optimization in its internal
implementation of the core datatypes. There are a lot of smart compiler/JIT
people that have worked hard on it over the years to optimize the internal
guts despite the naive design of the language due to its age and some poor
design decisions made ages ago that cannot be changed due to extreme backwards
compatibility requirements. Kind of like javascript and V8/SpiderMonkey :)

~~~
zackmorris
Thank you, I didn't know that MATLAB did that. I'm a huge fan of it and hope
that someday other matrix programming frameworks like TensorFlow, OpenCL and
CUDA move to a similar general-purpose syntax that frees the developer from
having to deal with the boilerplate associated with old OpenGL-style buffer
representations.

------
mpartel
This could be really useful for game development!

Certain kinds of networked games are built as a model, deterministically
updated by clock ticks and commands from the server, and a view layer on top
of that. And even model-level objects often monitor each other for changes.
Seems like a perfect fit.

Game objects often have graph-like (not tree-like) dependencies though. E.g.
two characters may want to move towards each other, creating a pointer cycle.
Ideally I'd want one to be "magically" updated if the other disappears or
changes state. I wonder if Skip has a good answer there, or if a good
language-level answer is even theoretically possible.

~~~
jverlaguet
It's definitely possible. You need to "break the cycle" by adding an
additional layer. You can encode any graph as an array of objects were you
replaced the pointers with indexes within the array. Of course, the
granularity (how big the arrays are) is up to you, and there is a tradeoff
here.

The other very important thing to note is that Skip has a memory model
suitable for that use case. Every function has a GC overhead, but that
overhead is non-contextual. Meaning, the GC will collect the memory for only
one function (instead of the entire heap). This is possible thanks to the
guarantees of the type-system.

~~~
mpartel
So, say I have: `characters = [A, B, ...]` and I want to maintain for each
character `x` the invariant: `x.target = (first y in 'characters' where
y.score > x.score)`

Can Skip maintain such a self-referential invariant automatically? Does the
answer depend on A and B's mutability?

(This "graph as array" escape hatch seems to come up in many language designs
with compile-time pointer analysis. It's useful, maybe even essential, but you
tend to lose some nice language features/guarantees with it, in my limited
experience.)

The GC thing sounds excellent!

~~~
vjeux
Not responding to your question directly but I just want to mention that if
you mark your class as mutable and use mutable instances, you can write code
as you’d expect from other languages. The only downside is that you can’t pass
those values to a memoized function unless you do a deep copy.

------
jhallenworld
The IBM DataPower XSLT compiler has automatic memoization, I remember the
compiler engineers implementing this. Of course XSLT is a functional language,
so it's not so hard..

Bob Morgan was behind it.

------
joedevon
Sounds a bit like netkernel to me.
[http://www.1060research.com/products/](http://www.1060research.com/products/)

------
mrkgnao
Can anyone comment on the similarities with (apparently somewhat less
sophisticated) laziness as in Haskell, or the memoisation/caching done by the
Nix package manager, or (perhaps most interestingly) the approach of the
Funflow library for Haskell[1]?

[1]: [https://www.tweag.io/posts/2018-07-10-funflow-
make.html](https://www.tweag.io/posts/2018-07-10-funflow-make.html)

------
bausshf
You should consider https for your site.

------
X6S1x6Okd1st
Does skip target js just through emscripten? Does it have good support for
browser interaction?

~~~
vjeux
We have js printed at the "OuterIST" phase right now. It's the phase just
before we sent to the native backend which will generate llvm. You can go to
the playground (
[http://skiplang.com/playground/](http://skiplang.com/playground/) ) and click
"JavaScript" to see the output.

We talked about doing it at an earlier phase in order to output JavaScript
closer to what the input code was, in a very similar fashion as BuckleScript
does. But we never gotten around to as we focused on the native backend.

We also have a prototype of a wasm backend using emscriptem.

Right now none of them are really usable as is but there's a lot of potential
there.

------
adamnemecek
The link to specification is broken.

~~~
tnowacki
Where do you see a link to that?

We had a specification in the works, but it fell horribly out of date. I
wasn't sure that we kept it around

~~~
adamnemecek
It's in the footer. CTRL + F for specification.

~~~
tnowacki
I'll look into removing it.

If you want an overview of language features/design. I'd start with the docs:
[http://skiplang.com/docs/hello_world.html](http://skiplang.com/docs/hello_world.html)

~~~
adamnemecek
Can I email you?

~~~
vjeux
You didn't ask me personally but feel free to email me, I've helped with the
open sourcing effort: vjeux@fb.com

------
falardea
"supports ergonomic asynchronous computation"... because you wouldn't want any
of your computations getting carpal tunnel or anything.

