DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on

100 Languages Speedrun: Episode 87: Sidef

Sidef is a prototype programming language, describing itself as inspired by Ruby, Raku, and Julia.

Installation on OSX

It's not that easy to run Sidef. It runs on Perl 5 platform and you can install it through CPAN, but you'll run into serious problems.

Perl version bundled with OSX is not really recommended, so I used brew version. Unfortunately if we do this:

$ brew install perl
$ cpan Sidef
Enter fullscreen mode Exit fullscreen mode

It will install sidef in ~/.cpan/build/Sidef-3.99-0/bin/sidef, but it's hardcoded to use system Perl.

So I needed two additional steps. Symlink ~/.cpan/build/Sidef-3.99-0/bin/sidef to some place in $PATH like ln -s ~/.cpan/build/Sidef-3.99-0/bin/sidef ~/bin/.

And edit sidef program to change its first line from #!/usr/bin/perl to #!/usr/bin/env perl so it will use the proper version, not the outdated one from OSX.

After all these steps, we're ready to go.

This is definitely something Sidef should just handle better. Oh and there's no VSCode syntax highlighting for Sidef. Often even very rarely used language have some.

Hello, World!

Hello, World is completely unsurprising. No ugly semicolons here.

#!/usr/bin/env sidef

say "Hello, World!"
Enter fullscreen mode Exit fullscreen mode
$ ./hello.sf
Hello, World!
Enter fullscreen mode Exit fullscreen mode

FizzBuzz

Sidef indeed looks like something between Ruby, Raku, and Julia.

#!/usr/bin/env sidef

(1..100).each { |n|
  if (n % 15 == 0) {
    say "FizzBuzz"
  } elsif (n % 5 == 0) {
    say "Buzz"
  } elsif (n % 3 == 0) {
    say "Fizz"
  } else {
    say n
  }
}
Enter fullscreen mode Exit fullscreen mode
  • range 1..100 goes from 1 to 100
  • there are no ... ranges
  • {|args| ...} blocks look like Ruby, but they're not used quite in the same way
  • if elsif else requires ()s and {}s

Blocks

Ruby blocks have special place in the language, and block argument is a separate thing from normal arguments. Not so in Sidef.

Sidef has very high level of syntax flexibility.

#!/usr/bin/env sidef

func twice(f) {
  say "Running it twice:"
  f()
  f()
}

var hi = { say "Hi!" }
twice(hi)
twice({ say "Hello!" })
twice { say "This does not work!" }

say ""
say "Iteration:"
{ |i| say "Block got: #{i}" }.each(10..12)
{ |i| say "Block got: #{i}" } << 13..14
for 15..16 { |i| say "Block got: #{i}" }
(17..18).each { |i| say "Block got: #{i}" }
{ |i| say "Block got: #{i+19}" } * 2
2.times { |i| say "Block got: #{i+21}" }

say ""
say "Single argument:"
60 |> { |i| say "Block got: #{i}" }
60 |> :inc |> { |i| say "Block got: #{i}" }
60 |> {|i| i + 2} |> { |i| say "Block got: #{i}" }
60 |> {_+3} |> { |i| say "Block got: #{i}" }
60 |> (:add, 4) |> { |i| say "Block got: #{i}" }
{ |i| say "Block got: #{i}" }(69)
Enter fullscreen mode Exit fullscreen mode
$ ./blocks.sf
Running it twice:
Hi!
Hi!
Running it twice:
Hello!
Hello!

Iteration:
Block got: 10
Block got: 11
Block got: 12
Block got: 13
Block got: 14
Block got: 15
Block got: 16
Block got: 17
Block got: 18
Block got: 19
Block got: 20
Block got: 21
Block got: 22

Single argument:
Block got: 60
Block got: 61
Block got: 62
Block got: 63
Block got: 64
Block got: 69
Enter fullscreen mode Exit fullscreen mode

Everything here works except for Ruby-style twice { say "This does not work!" } which does literally nothing, and I'm not sure why.

There are some debugging tools like -c compile the code into a Perl program and -D dump the syntax tree of a program, but the result is not really human readable.

I thought -k keep track of potential unsafe parser interpretations might say something about it, but that also doesn't say anything.

Having high degree of syntax flexibility is not that important for normal programming, but it's great for DSLs as it makes it easier for DSLs to pick something that works for them.

Especially the |> code is very cute.

Fibonacci

Sidef supports is cached for memoization for free, which is a common enough use case that I don't know why more languages don't do this. There are also ways to clear the cache.

#!/usr/bin/env sidef

func fib(n) is cached {
  return 1 if (n <= 2)
  fib(n - 1) + fib(n - 2)
}

(1..100).each {|n|
  say "fib(#{n}) = #{fib(n)}"
}
Enter fullscreen mode Exit fullscreen mode
$ ./fib.sf
fib(1) = 1
fib(2) = 1
fib(3) = 2
...
fib(98) = 135301852344706746049
fib(99) = 218922995834555169026
fib(100) = 354224848179261915075
Enter fullscreen mode Exit fullscreen mode

Operator Precedence

Sidef tries to be too cute with precedence rules.

#!/usr/bin/env sidef

say(2+3*4+5)
say(2 + 3 * 4 + 5)
say(2 + 3*4 + 5)
say(2+3 * 4+5)
Enter fullscreen mode Exit fullscreen mode

Prints 4 different results:

$ ./math.sf
29
25
19
45
Enter fullscreen mode Exit fullscreen mode

Sidef just doesn't have operator precedence, and instead tries to use spacing to determine what happens, in some completely insane way.

Not following standard operator precedence is the worst idea. Smalltalk tried that, and it killed the language. Every Smalltalk successor had to do all the painful things to unwind this stupid idea. And Smalltalk at least had simple consistent rules - operators always applies left to right. Sidef does something insane:

  • without spaces, operators apply right to left (2+3*4+5 is 2+(3*(4+5)))
  • with spaces, operators apply left to right (2 + 3 * 4 + 5 is ((2+3)*4)+5)
  • with some spaces, operators apply left to right when there are spaces, and have extra parentheses in un-spaced group

This disqualifies the language.

Wordle

Here's a Wordle game in Sidef:

#!/usr/bin/env sidef

var words = File.new("wordle-answers-alphabetical.txt").read.split
var word = words.rand
var guess = ""

while (guess != word) {
  print "Guess: "
  guess = STDIN.readline
  if (guess.size != 5) {
    say "Only 5 letter words allowed"
    next
  }
  {|i|
    if (word[i] == guess[i]) {
      print "🟩"
    } elsif (word.include(guess[i])) {
      print "🟨"
    } else {
      print "πŸŸ₯"
    }
  } * 5
  print "\n"
}
Enter fullscreen mode Exit fullscreen mode

It didn't go too bad:

$ ./wordle.sf
Guess: trial
πŸŸ₯πŸŸ₯πŸŸ₯🟨πŸŸ₯
Guess: maybe
πŸŸ₯🟨πŸŸ₯πŸŸ₯🟩
Guess: snake
🟨πŸŸ₯🟩πŸŸ₯🟩
Guess: chase
πŸŸ₯🟩🟩🟩🟩
Guess: phase
🟩🟩🟩🟩🟩
Enter fullscreen mode Exit fullscreen mode

Should you use Sidef?

No.

I support fun experiments, but Sidef has the absolute worst way of doing something as simple as adding numbers, making it pretty much unusable for anything.

An even bigger problem is what when you write some code, Sidef decides what it means, and half the time it will decide that you meant something else than you did. There's no error messages, no documentation, no syntax debugging tools, the code will just do whatever it feels like. Sometimes code does something else. Very often the code just doesn't do anything, and that's a lot more baffling.

I think to make Sidef usable for even casual play it would need at least:

  • fix operator precedence
  • have way better error messages
  • have some kind of "print how it parsed" option, that adds a lot of ()s and such to tell you what the hell Sidef thinks you just did
  • working OSX package

With these issues fixed, maybe Sidef could become something I could recommend playing with for a weekend or two. In its current state, just no.

Code

All code examples for the series will be in this repository.

Code for the Sidef episode is available here.

Discussion (0)