HN user

suzuki

176 karma

https://github.com/nukata

Posts22
Comments51
View on HN
github.com 6y ago

Show HN: Scheme with first-class continuations implemented in various languages

suzuki
4pts1
github.com 6y ago

Show HN: A Little Scheme in TypeScript

suzuki
2pts0
github.com 7y ago

Show HN: Cyg-Git Makes Cygwin's Git and GCC Usable from Go

suzuki
3pts0
github.com 7y ago

Show HN: Scheme with first-class call/cc in 700 lines of Go code

suzuki
1pts1
github.com 7y ago

Show HN: Tower of Meta-Circular Interpreters of Scheme

suzuki
4pts0
github.com 7y ago

How to Implement Scheme with Call/cc in Python

suzuki
3pts0
github.com 7y ago

Show HN: Wokring with Files in Golang LINQ

suzuki
3pts0
github.com 7y ago

Show HN: Scheme with first-class continuations in 300 lines of Python

suzuki
3pts0
github.com 7y ago

Show HN: An Experimental 'LINQ to Objects' in Go

suzuki
1pts1
github.com 8y ago

Show HN: A Lisp interpreter in C# 7

suzuki
30pts7
github.com 8y ago

Show HN: L2Lisp in Java: An experimental Lisp with lazy evaluation

suzuki
4pts0
github.com 8y ago

Show HN: A Common Lisp-Like Lisp-1 with TCO in TypeScript

suzuki
2pts0
github.com 8y ago

Show HN: A Common Lisp-Like Lisp-1 in Go with TCO and Partially Hygienic Macros

suzuki
5pts0
github.com 8y ago

Show HN: Lisp in Dart

suzuki
5pts0
github.com 9y ago

ToaruOS 1.0.4: The VMware Update

suzuki
2pts0
www.oki-osk.jp 9y ago

Scheme's first-class continuation implemented with nested While loops

suzuki
2pts3
www.oki-osk.jp 10y ago

A revised Lisp interpreter in Go

suzuki
54pts9
www.oki-osk.jp 10y ago

A Lisp interpreter in TypeScript

suzuki
58pts13
www.oki-osk.jp 11y ago

A fast Lisp interpreter in Dart

suzuki
64pts10
www.oki-osk.jp 11y ago

Lisp in Go or: How Go is significantly slower than Dart

suzuki
5pts4
www.oki-osk.jp 11y ago

Lisp in 1,200 lines of Dart

suzuki
4pts1
www.oki-osk.jp 12y ago

An experimental 'LINQ to Objects' in Go

suzuki
2pts1

If you have an interface with a single method, or something with a single varying piece of behavior, please strongly consider accepting a function instead.

Agreed. And if you prefer methods to functions, you can define methods on such a function type! In my LINQ in Go (https://github.com/nukata/linq-in-go), I define a higher order function type:

  type Enumerator[T any] func(yield func(element T))
and several methods on it, for example,
  // Where creates an Enumerator which selects elements by appling
  // predicate to each of them.
  func (loop Enumerator[T]) Where(predicate func(T) bool) Enumerator[T] {
        return func(yield func(T)) {
                loop(func(element T) {
                        if predicate(element) {
                                yield(element)
                        }
                })
        }
  }
Naturally, if you need another type parameter for a method, you must define it as a function. For example,
  // Select creates an Enumerator which applies f to each of elements.
  func Select[T any, R any](f func(T) R, loop Enumerator[T]) Enumerator[R] {
        return func(yield func(R)) {
                loop(func(element T) {
                        value := f(element)
                        yield(value)
                })
        }
  }
Now, with the function which converts a slice to an Enumerator,
  // From creates an Enumerator from a slice.
  func From[T ~[]E, E any](x T) Enumerator[E] {
        return func(yield func(E)) {
                for _, element := range x {
                        yield(element)
                }
        }
  }
you can write the following:
  seq := Select(func(e int) int { return e + 100 },
        From([]int{
                7, 8, 9,
        })).Where(func(e int) bool {
        return e%2 != 0
  })
  seq(func(e int) {
        Println(e)
  })
  // Output:
  // 107
  // 109

I agree with you. I wish Python 3 had strings as byte sequences mainly in UTF-8 as Python 2 had once and Go has now. Then things would be kept simple in Japan. Python 3 feels cumbersome. To handle a raw input as a string, you must decode it in some encoding first. It is a fragile process. It would be adequate to treat the input bytes transparently and put an optional stage to convert other encodings to UTF-8 if necessary.

I have written almost the identical Scheme interpreters in Ruby and Crystal: [1] and [2]. The biggest difference I have felt between them is the absence of good old Object, which can represent everything at runtime, from Crystal. I had to declare Obj and Val:

  class Obj
  end

  # Value operated by Scheme
  alias Val = Nil | Obj | Bool | String | Int32 | Float64 | BigInt
to define Cons Cell of Scheme:
  # Cons cell
  class Cell < Obj
    include Enumerable(Val)

    getter car : Val            # Head part of the cell
    property cdr : Val          # Tail part of the cell

    def initialize(@car : Val, @cdr : Val)
    end

    ...
  end # Cell
Note that you see generics, Enumerable(Val), and constructor arguments with '@' in the excerpt above.

As for performance, Crystal is faster than Ruby 8.6 times as interpreter and 39.4 times as compiler [3]. You can use Crystal as a superfast (and typed) Ruby interpreter, in a sense.

[1] https://github.com/nukata/little-scheme-in-ruby [2] https://github.com/nukata/little-scheme-in-crystal [3] https://github.com/nukata/little-scheme#performance

IANAL, but I believe you cannot put "(c) Your Name 2020" on Shakespeare's works in Japan and other countries. Their copyright acts protect the moral rights of authors[1][2]. So I think Zen's file headers would be violating the law in Japan and other countries.

[1] https://en.wikipedia.org/wiki/Moral_rights [2] http://www.japaneselawtranslation.go.jp/law/detail_main?id=2... (Copyright Act of Japan) Subsection 2 Moral Rights of Authors (Articles 18 to 20)

Now the abstract sequence type of "LINQ in Go" https://github.com/nukata/linq-in-go can be written as

  type Enumerator(type T) func(yield func(element T))
and the "Select" method can be written as
  func (loop Enumerator(T)) Select(f func(T) T) Enumerator(T) {
        return func(yield func(T)) {
                loop(func(element T) {
                        value := f(element)
                        yield(value)
                })
        }
  }
You can call this method with type-safety as follows. Yay!
  func main() {
        squares := Range(1, 3).Select(func(x int) int { return x * x })
        squares(func(num int) {
                fmt.Println(num)
        })
  }
  // Output:
  // 1
  // 4
  // 9
See https://go2goplay.golang.org/p/b0ugT68QAy2 for the complete code.

And, for generality, you should write the method actually as follows.

  func (loop Enumerator(T)) Select(type R)(f func(T) R) Enumerator(R) {
        return func(yield func(R)) {
                loop(func(element T) {
                        value := f(element)
                        yield(value)
                })
        }
  }
However, you will get the error message then:
  type checking failed for main
  prog.go2:17:33: methods cannot have type parameters
According to https://go.googlesource.com/proposal/+/refs/heads/master/des... this seems an intended restriction:

Although methods of a generic type may use the type's parameters, methods may not themselves have additional type parameters. Where it would be useful to add type arguments to a method, people will have to write a suitably parameterized top-level function.

This is not a fundamental restriction but it complicates the language specification and the implementation.

For now, we have to write it as https://go2goplay.golang.org/p/mGOx3SWiFXq and I feel it rather inelegant. Good grief

You are absolutely right. I have written Scheme interpreters in both languages. Compare https://github.com/nukata/little-scheme-in-ruby/blob/v0.3.0/...

  # Cons cell
  class Cell
    include Enumerable
    attr_reader :car
    attr_accessor :cdr

    def initialize(car, cdr)
      @car = car
      @cdr = cdr
    end

    # Yield car, cadr, caddr and so on, à la for-each in Scheme.
    def each
      j = self
      begin
        yield j.car
        j = j.cdr
      end while Cell === j
      j.nil? or raise ImproperListException, j
    end
  end # Cell
and https://github.com/nukata/little-scheme-in-crystal/blob/v0.2...
  # Cons cell
  class Cell < Obj
    include Enumerable(Val)

    getter car : Val            # Head part of the cell
    property cdr : Val          # Tail part of the cell

    def initialize(@car : Val, @cdr : Val)
    end

    # Yield car, cadr, caddr and so on, à la for-each in Scheme.
    def each
      j = self
      loop {
        yield j.as(Cell).car
        j = j.as(Cell).cdr
        break unless Cell === j
      }
      raise ImproperListException.new(j) unless j.nil?
    end
  end # Cell
and they will make the point clear. Ruby and Crystal are different languages, but you can translate your code from Ruby to Crystal line by line fairly easily.

For the performance boost, see https://github.com/nukata/little-scheme/tree/v1.3.0#performa... which shows times to solve 6-Queens on a meta-circular Scheme as follows:

* Crystal 0.34.0: crystal build --release scm.cr: 2.15 sec.

* Crystal 0.34.0: crystal scm.cr: 9.88 sec.

* Ruby 2.3.7: ruby scm.rb: 84.80 sec.

Compiled (and complex enough) Crystal code runs 39 times faster than the equivalent Ruby code in this case.

Crystal 0.34 6 years ago

I have just updated my Scheme interpreter in Crystal (https://github.com/nukata/little-scheme-in-crystal), which I used on the above benchmark test, along to Crystal 0.34.

The release notes of Crystal 0.34 say "Having as much as possible portable code is part of the goal of the std-lib. One of the areas that were in need of polishing was how Errno and WinError were handled. The Errno and WinError exceptions are now gone, and were replaced by a new hierarchy of exceptions." So I have modified

    rescue ex: Errno
      raise ErrorException.new(ex.message, NONE) if ex.errno == Errno::EPIPE
to
    rescue ex: IO::Error
      raise ErrorException.new(ex.message, NONE) if ex.os_error == Errno::EPIPE
though it is still dependent on POSIX. >_<

I timed each implementation in solving 6-Queens[1] on a meta-circular Scheme[2] (i.e. each Scheme interpreter ran a meta-circular Scheme interpreter to solve 6-Queens). Results are shown in [3]; the order of speed is observed as follows:

Go ≈ Java ≈ Crystal(Compiled) ≈ SBCL > C# ≈ PyPy > TypeScript(Node.js) ≈ Crystal ≈ Dart >> PHP > Python ≈ Ruby

[1] https://github.com/nukata/little-scheme/blob/v1.2.0/examples... [2] https://github.com/nukata/little-scheme/blob/v1.2.0/scm.scm [3] https://github.com/nukata/little-scheme/tree/v1.2.0#performa...

You can write your own LINQ in Go with a very short program. See https://github.com/nukata/linq-in-go for example.

Here is a self-contained excerpt:

  type Any = interface{}

  type Enumerator func(yield func(element Any))

  // Select creates an Enumerator which applies f to each of elements.
  func (loop Enumerator) Select(f func(Any) Any) Enumerator {
    return func(yield func(Any)) {
      loop(func(element Any) {
        value := f(element)
        yield(value)
      })
    }
  }

  // Range creates an Enumerator which counts from start
  // up to start + count - 1.
  func Range(start, count int) Enumerator {
    end := start + count
    return func(yield func(Any)) {
      for i := start; i < end; i++ {
        yield(i)
      }
    }
  }
Now you can write the following:
  squares := Range(1, 10).Select(func(x Any) Any { return x.(int) * x.(int) })
  squares(func(num Any) {
    Println(num)
  })
  // Output:
  // 1
  // 4
  // 9
  // 16
  // 25
  // 36
  // 49
  // 64
  // 81
  // 100
I'd say it is so elegant in Go!

This is a small interpreter of a subset of Scheme. It implements the same language as https://github.com/nukata/little-scheme-in-python (and also its meta-circular interpreter, https://github.com/nukata/little-scheme). As a Scheme implementation, it also handles first-class continuations and runs the yin-yang puzzle correctly.

  $ cat yin-yang-puzzle.scm
  ;; The yin-yang puzzle 
  ;; cf. https://en.wikipedia.org/wiki/Call-with-current-continuation
  
  ((lambda (yin)
     ((lambda (yang)
        (yin yang))
      ((lambda (cc)
         (display '*)
         cc)
       (call/cc (lambda (c) c)))))
   ((lambda (cc)
      (newline)
      cc)
    (call/cc (lambda (c) c))))
  
  ;; => \n*\n**\n***\n****\n*****\n******\n...
  $ little-scheme-in-go yin-yang-puzzle.scm | head
  
  *
  **
  ***
  ****
  *****
  ******
  *******
  ********
  *********
  $

This LINQ implementation is unique in that it is not related to any particular data structure.

The LINQ code in C# found at https://docs.microsoft.com/dotnet/api/system.linq.enumerable... can be written in Go with this implementation as follows:

  // Generate a sequence of integers from 1 to 10 and then select their squares.
  squares := Range(1, 10).Select(func(x Any) Any { return x.(int) * x.(int) })
  squares(func(num Any) {
      fmt.Println(num)
  })
Here the function Range is defined as follows:
  // Range creates an Enumerator which counts from start
  // up to start + count - 1.
  func Range(start, count int) Enumerator {
      end := start + count
      return func(yield func(Any)) {
          for i := start; i < end; i++ {
              yield(i)
          }
      }
  }
Note that Enumerator is just a function type defined as func(func(Any)). The space complexity is O(1) and you can yield values infinitely if you want.

Thank you. I appreciated especially the pattern matching feature of C# 7 while programming the interpreter. For example:

        /// <summary>Evaluate a Lisp expression in an environment.</summary>
        public object Eval(object x, Cell env) {
            try {
                for (;;) {
                    switch (x) {
                    case Arg xarg:
                        return xarg.GetValue(env);
                    case Sym xsym:
                        try {
                            return Globals[xsym];
                        } catch (KeyNotFoundException) {
                            throw new EvalException("void variable", x);
                        }
                    case Cell xcell:

I am afraid they omitted "?" at

  type Partial<T> = {
      [K in keyof T]: T[K]
  }
in their announce. It should be
  type Partial<T> = {
      [K in keyof T]?: T[K]
  }
to make
  interface Thing {
      foo: string;
      bar: number;
      [baz]: boolean;
  }

  type PartialThing = Partial<Thing>;
equivalent to
  interface PartialThing {
      foo?: string;
      bar?: number;
      [baz]?: boolean;
  }

I think the invention of Python 3 was an unhappy thing. It seems true that "there are still a lot of companies on 2.7" and it would be natural that they think "the last decade of Python work isn't really useful".

The basic incompatibility between Python 2 and 3 comes from Unicode strings. The design of Python 3 might be adequate ten years ago when there are many character encodings in the world. However, they began to converge to UTF-8, you know. It would be rational now to use byte strings transparently overall just like in Golang. What people wanted would be a modernized Python that is compatible with Python 2 and it would have been feasible.

I still use Python 2.7 and all I need with strings is UTF-8 byte string nowadays in Japan. If you read Japanese, read the following blog to know the current circumstances:

https://note.mu/ruiu/n/nc9d93a45c2ec

And note that Golang is getting popular in Japan, which uses UTF-8 byte strings solely. Python 2.7 with byte strings as default has a good chance to evolve into a more elegant language :-)

There is another Lisp implemented in 42 languages (including Ceylon, Dylan, Oz, Pike, Scratch, Smalltalk, SML etc.):

https://github.com/zick/ZickStandardLisp

The benchmark results of 42 implementations are very impressive:

http://blog.bugyo.tk/lyrical/wp-content/uploads/2014/12/stag... http://blog.bugyo.tk/lyrical/wp-content/uploads/2014/12/stag... http://blog.bugyo.tk/lyrical/wp-content/uploads/2014/12/stag...

which are discussed in Japanese at:

http://blog.bugyo.tk/lyrical/archives/2024

I hope you will be interested in the C#/LINQ codes found in following pages:

  "Why Functional Programming Matters" solved with C#
  http://www.oki-osk.jp/esc/cs/whyfp.html
  http://www.oki-osk.jp/esc/cs/whyfp2.html
  http://www.oki-osk.jp/esc/cs/whyfp3.html