HNHacker News
TopNewBestAskShowJobs

suzuki

176 karma · joined January 25, 2014

https://github.com/nukata
submissionscomments
suzuki··on When to use generics
> 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
suzuki··on The Strand Programming Language
It is a "mojibake" of 三個和尚沒水喝[1].

[1] https://en.wikipedia.org/wiki/Three_Monks

suzuki··on Pip has dropped support for Python 2
From a Japanese perspective, it also sounds very unlikely. Go with UTF-8 strings is successful nowadays in Japan.
suzuki··on Pip has dropped support for Python 2
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.
suzuki··on Switch from Ruby to Crystal
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

suzuki··on Statement Regarding the Zen Programming Language
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)

suzuki··on The Next Step for Generics
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

suzuki··on Ruby vs. Crystal Performance
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.

suzuki··on Python 2.7.18, the last release of Python 2
I totally agree with you. I hope we still have a chance to do it well with PyPy.
suzuki··on Crystal 0.34
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. >_<
suzuki··on Crystal 0.34
Crystal is comparable to Java in performance. I am impressed with it. See https://github.com/nukata/little-scheme#performance and https://github.com/nukata/little-scheme#performance-on-the-t...
suzuki··on Towards Crystal 1.0
In my experiment, the Crystal version (https://github.com/nukata/little-scheme-in-crystal) was 38x faster than the Ruby version (https://github.com/nukata/little-scheme-in-ruby) and comparable to the Java version (https://github.com/nukata/little-scheme-in-java). Indeed, it is in the fastest group. See https://github.com/nukata/little-scheme#performance
suzuki··on Show HN: Scheme with first-class continuations implemented in various languages
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...

suzuki··on Loopless Programming
Good point! I'm looking forward to generics in Go.
suzuki··on Loopless Programming
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!
suzuki··on Show HN: Scheme with first-class call/cc in 700 lines of Go code
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
  
  *
  **
  ***
  ****
  *****
  ******
  *******
  ********
  *********
  $
suzuki··on Show HN: An Experimental 'LINQ to Objects' in Go
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.
suzuki··on Windows file access performance compared to Linux
If you had used WSLGit (https://github.com/andy-5/wslgit), you could use Cyg-Git (https://github.com/nukata/cyg-git) instead. It provides a "Git for Windows" virtually for Go and VSCode. You only need Cygwin and its packages, including Cygwin's git.
suzuki··on Show HN: A Lisp interpreter in C# 7
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:
suzuki··on Show HN: A Lisp interpreter in C# 7
Thank you. This Lisp is a translation of my other Lisp in 1200 lines of Dart (https://github.com/nukata/lisp-in-dart), which is, in turn, a digest or refinement of my old L2Lisp in Java (https://github.com/nukata/l2lisp-in-java). I did my best to optimize it for C# 7.
suzuki··on Show HN: A Lisp interpreter in C# 7
I wonder if it might be unfair to include the byte-compiled results (with .fast and .elc files) of CLISP and Emacs Lisp to the table.
suzuki··on Announcing TypeScript 2.9 RC
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;
  }
suzuki··on Ask HN: Why is Python struggling to rasie funding?
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.

suzuki··on Numpy: Plan for dropping Python 2.7 support
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 :-)

suzuki··on Mal – Make a Lisp, in 68 languages
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

suzuki··on A Preview of C# 8 [video]
Glad to see this Dart feature appearing in C#:

  Asynchronous Streams
Now you can "yield" Task<T> (~ Future<T> in Dart) and construct LINQ chains in an asynchronous fashion. I hope it streamlines reactive programming greatly.

[1] Dart - Asynchronous Programming: Streams https://www.dartlang.org/tutorials/language/streams

[2] .NET Futures: Asynchronous Streams https://www.infoq.com/news/2017/05/async-streams

suzuki··on What’s Revolutionary about Flutter
I have recently tried Flutter and found it light and smooth. The article explains the reason well.

I think Flutter is promising.

suzuki··on Flutter – A mobile app SDK for iOS and Android
> It's Java syntax mostly with modern features and much less boilerplate code.

Exactly. Dart is a sort of rational and modern variant of Java/C# language family. It is excellent in handling async/await. I find Dart Code extension for Visual Studio Code fairly useful for developing Flutter. Dart Code as well as IntelliJ supports for flutter hot reload.

https://marketplace.visualstudio.com/items?itemName=DanTup.d...

suzuki··on Why Functional Programming Matters (1984) [pdf]
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
suzuki··on The Case Against Python 3
I totally agree with you.

> ... one is called "str" and represents a sequence of bytes ... The former is used while interacting with network protocols and files ... Python 3 actually got this wrong for years ...

Actually some Japanese have suffered from this wrong design of Python 3. If you read Japanese, please see http://www.oki-osk.jp/esc/python/upload-cgi/v2.html#3

Page 1 of 2Next →