Quantcast
Channel: Hacker News
Viewing all articles
Browse latest Browse all 25817

Show HN: Skov – A visual programming environment

$
0
0

Skov is a visual programming environment based on Factor.


Download the version of Skov adapted for your platform:

There is no installation process. Just extract the Skov folder from the zip or dmg archive and run the program.

If you are on a Mac and Skov does not work, you need to right-click the app and select Open or disable Gatekeeper.


You will see this window.

The thing on the right is used to navigate the Skov environment. Each dot is actually a plus button in disguise. Click the bottom one or press N to create a new word. (A word is a reusable part of a program, it is generally called a function in other programming languages.)

You can now enter the name of your word. Type hello and press .

The red cross indicates that there is a problem with this word, it cannot be executed. Of course there's nothing in it! One of the dots on the left hides a plus button that allows you to add a word to the definition of your hello word. Click it or press W, type print and press . The last button on the left allows you to add some text. Click it or press T, type "Hello world!" and press .

The two nodes just beg to be connected together. Hello world! has a connector on the bottom, which indicates that it outputs some data (the "hello world" string of characters), while print has a connector on the top, which indicates that it takes some data as input. Inputs are always on the top, outputs are always on the bottom. Use your mouse or trackpad to connect the two nodes together.

The arrow next to the hello word is the result button. Click it or press to see the result. You have just created a program that writes "Hello world!" to the screen.


Now press N to create a new word and call it addition.

Press W to enter the add word. Press W again to enter the 1 word and then again to enter the 2 word. Connect them together. Now enter the display word and connect it to add.

Click the result button and you should see the number 3.

Why did we use display and not print? Because print is used to print text, while display is used to display any object (including text, try it on the above example to see the difference).


Press N to create a new word and call it process text. Press I to enter an input and call it text. Press O to enter an output and call it text also. You should be able to reproduce the rest of the code easily.

To enter Skov code, you don't actually have to first enter all the nodes and then connect them together. You can do it in one step. If you place your cursor on a connector and press W, the new word will be linked to this connector automatically. This allows you to enter Skov code almost as fast as in conventional programming languages.

>upper converts some text to upper case.

reverse simply reverses a sequence (including text, which is a sequence of characters).

append takes two sequences and sticks them together in one sequence.

This is the first word you define that has inputs and outputs, which means you can use it to process data. The previous words you defined just displayed something on the screen. This one doesn't display anything, but you can use it in another word. Let's do it.

Define a new word called text test and enter this code.

Click the result button and you should see "((VOKS))" because the input text skov was converted to upper case, reversed, and double parentheses were added on either side, as requested.


Look back at process text and see how easily you can chain operations. When you look at Skov code, you can imagine data flowing from top to bottom through a chain of operations, as if data was attracted by gravity.


The next example is going to introduce an extremely important concept.

Let's say we have some text and we want to keep only the letters that are represented by an even number (every letter is actually represented by a number behind the scenes). We do it like this:

Notice that the connection between even? and filter is different. This is because filter works in a special way. The first input to filter is a sequence (a sequence of characters in this case) but the second input is a word. filter takes a sequence and a word and uses this word to filter the sequence.

You can imagine the unconnected input of even? being connected to each individual letter of the text successively.


Now we want to remove every letter that comes after "m" in the alphabet. Because every letter is represented by a number, we can do (if we know that "m" is number 109):

reject works like filter except that it uses the given condition to discard certain elements of the sequence, instead of keeping them. What you can see here is that the condition doesn't have to be a single word (like even? in the previous example) but can be more complex code (in this example, "more than 109").


We can now use this principle to process some text files.

We want to reverse the order of the lines of text in a file. The last line will become the first and the first will become the last. That's easy:

(Of course, use the path to a real text file on your computer.)

change file lines is a word that takes three inputs: the path to a text file, the character encoding (UTF-8 in this case; it is the standard that tells you which letter is represented by which number), and a word (or some more complex code) that is going to take the sequence of lines of text and do something to it.

If your text editor is intelligent enough to reload files automatically, you will see that every time you click the result button, the file is reversed.


We can also easily remove all empty lines in a file

harvest is a word that removes every empty sub-sequence in a sequence.


How do we remove all the lines of a file that contain "hello"?

This one is great! We've seen that change file lines is one of these special words that can take a piece of code as an input, and reject is also one of them. And now they're used together.


I really need to emphasize the power of this.

change file lines is a word that can process a text file, but it doesn't do any specific thing to a file. It's a configurable word, it can do many different things depending on the code you give to it.

filter and reject can filter a sequence in many different ways, depending on the code you give to them.

When you want to remove all empty lines in a file, if you know that there is already a word called harvest that removes empty sub-sequences in a sequence and that there is already a word called change file lines that modifies a file by treating its lines as a sequence, the solution is just a matter of combining these two words together. The system is very modular. For many problems that you will need to solve, you will find that the necessary parts already exist and you just have to combine them in a certain way.


Now some math...

These are the hyperbolic sine and cosine functions.

Go to Wikipedia to see these functions written in traditional mathematical notation and compare them to the Skov code. Do you agree that this code is a more beautiful representation of these functions than mathematical notation?


And the inevitable factorial function...

This one is interesting for two reasons.

The first is the if word. It is again a word that can take some code as input. The first input is normal, it just takes a boolean value (true or false), but the two other inputs take pieces of code. The value given to the first input determines which of these two pieces of code is going to be executed.

The second reason is the fac that you see in the middle of the definition. This is the factorial function whose code you're looking at. It means that fac uses fac in its definition. This is called a recursive definition. fac is going to be called recursively as many times as necessary to compute the result.


It's now time to talk about objects. An object is a container for several pieces of data. For example, an object can contain two numbers, or one number and a sequence, or three other objets. An object belongs to a certain class. You can create a new class of objects by clicking the second dot on the right or by pressing K. Call it point.

By clicking the only dot on the left or pressing S you can add what we call slots to this class. A slot can contain a piece of data. Let's create two slots called x and y.

We can now create point objects that we can use to represent coordinates on a screen. When you define a class, the following words are defined automatically for you:

The first is a constructor, it takes two numbers and gives you a point object. The second is a destructor, it takes a point object and gives you the value of every slot. Next we have two accessors for the x and y slots. They take a point object and give you the value of the slot. And the last two are mutators, they take a point object and a number and they give you a point object with the value inserted in the corresponding slot.


We want to compute the distance between two points. Define a new word called distance.

You will see that there are four dots on the left that let you insert a constructor, a destructor, an accessor or a mutator. Or you can insert them by pressing C, D, A and M, respectively. You should be able to reproduce this code:

sq is the square function and sqrt is the square root function.

Look at this code carefully. Do you recognize the Pythagorean theorem that you learnt at school? Do you agree it looks better now?


To test our distance word, let's make a new word called distance test. You will notice that when you try to insert distance inside, this happens:

Skov tells you that there are two words called distance in the system and it asks you to choose one. Having several words with the same name is not a problem in Skov because words are sorted in vocabularies. A vocabulary contains words, classes, and sub-vocabularies, they allow you to organize your code neatly instead of defining every word in the same big bag of words. All the words you have defined until now have been defined in the vocabulary called scratchpad.

Select the second distance by pressing and .

Now you can test this code:

See how we use two constructors to create two point objects that we pass to distance.


By now we have encountered the three types of nodes that you can create on the right-hand side of the window:

  • Words are a code abstraction. A word has inputs and outputs and it can process data. It can also perform an action like displaying something on the screen and modifying files. Most of the time, an input takes a piece of data, but sometimes, an input can take a piece of code instead. This allows a word to use an external piece of code to process the data. A word can be used in the definition of another word and this is how you build programs: words calling other words, calling other words, etc.
  • Classes are a data abstraction. A class describes a type of object by specifying the name of the slots that this object has. An object is just a way of combining several pieces of data together. An object doesn't do anything, or compute anything or process anything, it's just data. You can put objects inside objects inside objects, this is how you handle complex data.
  • Vocabularies are used to organize your code like folders let your organize your files. Vocabularies can contain words, classes and other vocabularies.

Vocabularies can be created by clicking the first dot on the left or by pressing V. The following example shows one class and several words grouped inside a vocabulary called simulator.


If you know some other programming languages, I would like to take the time to highlight some differences with Skov.

I have just explained that words, classes and vocabularies do three different things: describing computation, describing data and organizing code. In other languages the distinction is not so easy. In object-oriented languages like Smalltalk and Java, the class is a concept used to describe data and organize code. You have to define your "words" (called "methods") inside classes. Classes take the role of vocabularies. The Scheme language relies on the concept of lexical closure, which is used to describe computation, hold data and organize code. Words take the role of classes and vocabularies. I believe having three separate concepts to describe computation, describe data and organize code makes things easier.

Most programming languages have a lot of syntax that you have to learn. They have parentheses, curly brakets, square brakets, semicolons all over the place. There is a syntax to call a function, another one to use a method of an object, another one to access an element of a list, etc. Skov does have some syntax (there is a special syntax for constructors, accessors, etc.) but much less than traditional languages.

Traditional textual languages force you to read the code from left to right, which, depending on the language, either means from the last function executed to the first ("the result is the sum of the squares of the even numbers in the list") or from the first function executed to the last ("take a list, find the even numbers, square them, compute the sum, return the result"). Python even forces you to mix the two approaches, which is horrible. You should be able to read code both ways because both approaches are useful in different circumstances. In Skov it is as easy to read code from top to bottom (from inputs to outputs) as from bottom to top (from outputs to inputs). Even better, it is as easy to write code starting from the inputs as from the outputs and only a visual language can do that.

Now we reach something even more fundamental. Traditional textual languages are one-dimensional. A program is just a long chain of characters. It means that you can easily compute something and pass the result to one other computation (you can chain functions together) but if you want to pass the result to several other computations it's more difficult. You have to give a name to your result and then use this name in several places to refer to your result. Skov is two-dimensional and this is why a result can be passed to several words directly (look at hyperbolic sine, hyperbolic cosine and factorial above).

In languages that have a module system (a module being the same as a Skov vocabulary, a thing that groups related functions together), imports are always a problem. The most common functions don't need to be imported because they are in a "base" module that is always active. If you want to use other functions, you have to write an import statement at the top of the file. You either import a whole module and you can use every function in it, or maybe you have to call your functions by module.function(), or you can choose to import just one function from a module. If a module name is too long, you can chooe an abbreviation and write md.function(). This get tiresome very quickly! Having a module system is great because functions (words) are neatly organized into separate drawers instead of being in one big bag. Skov lets you use every word without asking you to tell it from which vocabulary you want to take it, as long as there is no ambiguity. It is only when two words have the same name that Skov will ask you to choose the one you want.

In traditional languages, the code is just one long chain of characters and every time the compiler or interpreter reads it, it has to find that "cos" (for example) means the cosine function that is defined in the "math" module. The code just contains the three letters "cos". In Skov (and I'm speaking of the code that resides in the image file here, not the code exported as text), every word that you see on the screen inside a word definition contains a direct link to the word that it represents. The compiler doesn't have to find the word you want to use every time, the information is stored permanently inside your code.

Another very fundamental thing. In a textual language, the programmmer and the system (compiler or interpreter) work with the same file but they don't have the same needs. The compiler would like to have a lot of information about everything: the module of every function, the type of every input and output. If all this information was present in the text file, the programmer would find it extremely hard to read. The programmer needs concise, compact code, with short names, even if it means there is a lot of implicit stuff going on. Actually, the programmer would be interested in having access to the same extensive information as the compiler (the module for each function, the type of each input and output), but on demand, not displayed all the time. Textual languages have to find a compromise to be readable so a lot of the information about the program is implicit and has to be reconstructed by the programmer and the compiler. In a visual language, the program can contain all the necessary information so everything is explicit. Only part of that information is displayed at a time to keep things simple but the programmer can have access to all the information on demand.


I don't want to say that visual programming is always better than programming in text files (the code for Skov is written in text files after all), but I want to show that textual languages have a lot of limitations that they will never overcome because of these fundamental constraints:

  • A text file forces you to read code in a certain direction.
  • A text file is one-dimensional so you can't do direct links from one place to another.
  • The programmer and the compiler/interpreter work with the same file so they see the same thing.

When you go, or go back, to programming in textual languages, I want you to be aware of these limitations and why they are here: because of the text file paradigm.

Every now and then, someone comes up with a new programming language with a slightly different syntax so that a particular type of operation is easier to express. But there are always compromises so inevitably other types of operations will be harder to express and there is no way to solve the problem while staying inside the text file paradigm.


Where does the name come from?

Skov means forest in Danish because Skov contains a lot of trees.


What is the goal of the Skov project?

Imagine that you don't know any programming language and that you don't have any programming tools on your computer, but you want to write a program to do a relatively simple task. Then Skov will be the easiest tool to use. It's going to be the easiest to install, the easiest to learn, the easiest to understand and the easiest to get working. This is the goal of the project.


How is Skov implemented?

Skov is based on Factor, which is a concatenative programming language (or stack language). Factor is not just a programming language, it's also a dynamic environment where you can see and modify everything on the fly. (It's inspired by Smalltalk.) There is a virtual machine written in C++ that executes the Factor code that makes the environment. The compiler that converts Factor code to machine instructions is itself written in Factor and is part of the dynamic environment. All the user interface is programmed in Factor except the low-level OpenGL primitives. It's really a fantastic system!

Skov is entirely coded in Factor. Most of the code is for the user interface. The rest is quite simple.


How is my Skov code compiled?

Skov code (by that I mean the hierarchy of objects that describe the tree that you see on the screen) is sort of converted into Factor code but not how you think. Skov does not generate code as a string of characters. The initial hierarchy of objects is converted into another hierarchy of objects that the Factor compiler can understand. The compiler then converts that to different hierarchies of objects to perform its successive optimizations. It's objects all the way down and there's never any text involved. It's a true visual system and there's no cheating.


Why did you choose this strange language, Factor, or whatever, to implement Skov? Woudn't it be better to recode everything in Jav...

Stop it! Stop it immediately!

I promise Factor was the best language to implement Skov, by far. It's a standalone system with useful libraries, it has its own virtual machine, it has an efficient compiler, it's very well designed, and it's very modular.

Factor really is a great system but nobody uses it because nobody likes the language. I hope Skov will make this great system useful to a much wider audience.


But couldn't Skov be coded in Skov?

In theory, yes! Maybe in the future it will be, but it's not a priority.


How can I run Skov in my web browser?

Why does everything have to run in a web browser these days?


Can the user interface, the documentation and the language itself be translated into other languages than English?

I think making the user interface multilingual but keeping English words in the programming language makes no sense, so everything would have to be made multilingual at once, but that would be an absolute nightmare to implement. Making Skov multilingual would be absolutely fantastic but there would be so much work to do that I doubt it will ever happen. Using a lingua franca is so much easier.


How can I contribute?

The project is hosted on GitHub. There you can report bugs and contribute to the development.


How mature is this project?

The Skov project started on 7 August 2015 so it's not very mature. But Skov is based on Factor, which has been developped since 2003 and is a mature system.


How can I share my Skov code with other people?

If you press D, Skov will export all you code as text files in the "work" folder. There will be a file for each vocabulary. If you send theses files to someone, he can import them in Skov by placing them in his own "work" folder and pressing L.


Viewing all articles
Browse latest Browse all 25817

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>