HNHacker News
TopNewBestAskShowJobs

yiransheng

1,489 karma · joined February 3, 2013

submissionscomments
yiransheng··on Was BASIC that horrible or better?
Trying to compile Basic is actually a bit trickier than just implementing an interpreter indeed. Like the FOR statement example you mentioned. Another messy situation is non-lexical FOR/NEXT like here [1], where NEXT can be encountered before a FOR statement, fairly certain I did not get it entirely right there.

Original Dartmouth BASIC had only 14 statement types, and it takes very little efforts to enumerate and implement all of them, even if you are just starting on a compiler journey and making things up suboptimally along the way.

The bonus upside for choosing BASIC as your first compiler project is that you get decades of vintage test programs to play with like this one [2].

[1] https://github.com/yiransheng/basic_rs/blob/ddd64e2eacfc2b36...

[2] Game of Life compiled to warm: http://subdued-afternoon.surge.sh/

yiransheng··on How can some infinities be bigger than others?
What you described is called Continuum hypothesis[1]. Surprisingly it cannot be proven to be either true or false from(is independent of) ZFC set theory.

[1] https://en.m.wikipedia.org/wiki/Continuum_hypothesis

yiransheng··on Rayo's Number
This number is the result of a "large number duel"[1] between MIT Associate Professor Agustin Rayo (The Mexican Multiplier) and Princeton Associate Professor Adam N. Elga (Dr. Evil)[2]. Possible the world record largest number by some standards.

[1] https://www.scottaaronson.com/writings/bignumbers.html

[2] http://tech.mit.edu/V126/N64/64largenumber.html

yiransheng··on The unlikely resurgence of Dungeons and Dragons
I would recommend check out urealms[1]. It is a improv/role playing show running on rules similar (but simplifed) DnD rules. The format is a group of twitch personalities steam their gameplay for a live audience, contents including improv jokes, pre-planned dramma, pre-made animation cut scences and at the center of it, dice-roll based table top game play.

The primary tool used for the show is tabletop simulator[2], which is a game designed for any tabletop games: cards, dices, chess etc. and can be customized quite a bit. It's mostly a low-tech solution, replicating physical acts of throwing dices, moving pieces and shuffling cards on a virtual table. In addition, the creators of URealm also invested in other custom softwares and artwork. However, the show is a for-profit project seeks to provide entertainment value for an audience rather than purely for the enjoyment of participants.

[1] https://www.urealms.com/ [2] https://store.steampowered.com/app/286160/Tabletop_Simulator...

yiransheng··on Bytecode compilers and interpreters
Oh nice. I have been writing a BASIC bytecode interpreter (and a compiler for WebAssembly)[1] in Rust.

It's a bit slower to get rolling than Python - especially function value representation and dynamic dispatch, unlike Python, could not just make a callable object on the fly. Handling function/procedure calls in a stack VM is definitely a fun exercise, and there seems to be many ways to skin the cat. Shared vs. dedicated data stack across functions, adding registers (unlimited number if required) etc.

[1] https://github.com/yiransheng/basic_rs

yiransheng··on Why Do React Hooks Rely on Call Order?
Hooks reminds me of tensorflow scopes a little bit, in python:

    with tf.variable_scope("foo"):
        with tf.variable_scope("bar"):
            v = tf.get_variable("v", [1])
            assert v.name == "foo/bar/v:0"

`v` tensor here will have a generated human readable unique name here: "foo/bar/v:0"

It seems with hooks, react uses call order to derive the unique "leaf" hook id for runtime resolving its implementations. However, it would be nice if react hooks can automatically provide a similar human readable "hook id" (even if only for dev/debug build).

    function useWindowWidth() {
      const [[width, setWidth], stateId] = debug(useState);

      assert(stateId === "useWindowWidth/state/:0");

      useEffect(() => { ... });
      const [_, effId] = debug(useEffect, () => { ... });

      assert(effId === 'useWindowWidth/effect/:1");

      return width;
    }
This will definitely help for nested custom hooks..
yiransheng··on When Functional Programming Isn't Functional
In this case, I think Java's lack of tuples adds quite a bit of syntactic noise, a similar version in rust (another language with method chaining based iterators):

    let generated: Vec<i32> = 
        itertools::iterate((0, 1), |&(a, b)| (b, a + b))
            .map(|s| s.0)
            .take(10)
            .collect();

Arguably, this is closer to the matrix form of Fibonacci series[1]. In addition, the same code structure can generalize to any series with a recursive definition in the form of: a_n = f(a_{n-1}, a_{n-2}, ..., a_{n-k}) - which by some standard expresses intent better. Especially, for some k, the mental overhead of a loop version is too much to bare :)

[1] https://wikimedia.org/api/rest_v1/media/math/render/svg/c795...

yiransheng··on How fast is AMP really?
Really impressed! Took a quick look at page source, a couple of observations on why it's fast:

1. Plain, small html - page size is 6.1 kb (request took 700ms for me, I guess the server side rendering is pretty fast too), css file 11kb; note html, css and js are all properly minified

2. Font-awesome and jquery at the bottom of page to not block page rendering, both loaded throught cdn (although a fallback jQuery exists)

3. Site's own js is only 5.6k

4. No large static assets, only image on page is an svg dlogo

5. Did not check with js off, but saw hints of decent fallbacks for no-script

yiransheng··on Why we rewrote Lua in JS (2017)
The list of niceties of lua from the article:

> First class functions and closures

> A versatile data structure: the table

> Vararg expressions

> Lexical scoping

> Iterators

> Coroutines (see below)

Well, looking at modern day js:

* First class functions and closures, check

* the table: a combination of `Array`, `Map` and `Map<Number, T>` can prettify much cover typical usecases of lua tables, so js is close in this regard

* Vararg expressions, check

* Lexical scoping, check

* Iterators, check

* Coroutines, with libraries and abstractions on top of generator functions

In addition, in js land, other paradigms of concurrency exists, such as CSP (goroutines), FRP (Rxjs and co). Of course, these can be implemented in Lua as well, but the libraries may not quite as battle-tested.

The author also mentions a sane coercion system; this still true, but ever since "use strict", unexpected coercions can mostly be avoided in js.

On the other hand, js has:

1. Typescript and static typing (a bit unfair to consider this js, but ts gets access to a large portion of high quality npm packages), flow is also great

2. Multiple mature echosystems of UI frameworks (React) - for instance if you need a date picker or an autocompleter, it's just `yarn add` away; I doubt enough such libraries exist for browser side lua yet

3. Tooling support - webpack, prettier, postcss and so on

I like lua, but I feel it does not offer much in addition to what modern js already provides. Of course bring more languages to the browser is always a good thing, on the other hand I do feel this is a tough sell to the larger community.

yiransheng··on Update on Async Rendering
Interesting mention of create-subscription[1], first time I have heard of such a library/api. Seems it's part of official react repo. Looks like a great piece of utility for interfacing rxjs/xstream etc. Observables. I guess this may replace recompose[2]'s Observable utilities as it's officially endorsed by react team.

Current react ecosystem broadly speakings splits on state management solutions in two styles: 1. Single Observable-like source of truth (redux store) 2. Multi-Observables (relay, appolo, mobx)

It will be interesting to see if `create-subscription` catches on, and whether library authors will start exporting subscriptions as public apis.

[1] https://www.npmjs.com/package/create-subscription

[2] https://github.com/acdlite/recompose/blob/master/docs/API.md...

yiransheng··on D3.js 5.0 is out
Finally, `d3.create`[1] - it has been a minor pain pointer in d3 to create a detached selection for quite some time :)

[1] https://github.com/d3/d3-selection/blob/master/README.md#cre...

yiransheng··on Using Prettier to format your JavaScript code
Prettier is such an awesome tool! The best part of it is formatting jsx code - being a mixture of xml and js code, formatting it manually is such a pain. Incidentally, the existence of prettier is also a major reason React stack is my goto choice in Ui works - although it seems prettier support for Vue is [coming](https://github.com/prettier/prettier/issues/2097)
yiransheng··on How to Study Mathematics (2017)
Some personal thoughts: the key for me at least, is a bit like the hammer-nail mentality. An software engineer for example has a (small) mathematical toolkit (a hammer), sometimes forcing your problem at hand into the shape of nail is sufficient to solve it.

A dump example, consider the problem of detect if two date ranges overlap (for a calendar app perhaps). By recognizing a date range, DateRange(a, b) as an interval on the real line: [a, b] or a set {x | a <= x <= b}, the problem becomes find if two sets' intersection is empty. Set {x | a <= x <=b } and {x | c <= x <= d}'s intersection is {x | (x >= a and x >= c) and (x <= b and x<=d)} which simplifies to {x | max(a, c) <= x <= min(b, d) }. Therefore, the set is empty iff max(a, c) > min(b, d).

Translating the problem into mathematical structures here (intervals), and manipulate them with math tools (inequalities) would count as applied math I guess. By having a repository of math knowledge and an eager attitude to look for opportunities to apply them, one can find success in solving practical problems. In this context, rigor and formal proofs are less important and math intuition is paramount.

An real world example I like to motivate myself with is Richard Feynman and connection machine[1], a story about Feynman using partial differential equations to model on boolean circuits. I do believe even with a limited set of mathematical tools, by trying them on every problem you encounter, you can still get good results. Something like, if one squint hard enough, you can find a monad anywhere :)

[1] http://longnow.org/essays/richard-feynman-connection-machine...

yiransheng··on Kotlin: The Problem with null
In languages like Haskell and Scala that supports `do` / `for` comprehension sugar, this could be done without the nesting:

  fn :: Maybe Int -> Maybe Int -> Int
  fn q z = fromMaybe 0 $
    do 
      q_in <- q
      z_in <- z
      return (q_in + z_in)
yiransheng··on Global Mutable State
In FP world, there's a somewhat advanced solution to this, Free Monad DSL/Interpreter pattern. I first found out about it in this talk: [https://www.youtube.com/watch?v=fU8eKoakd6o](https://www.you.... There's an example of a pure functional game ai DSL in it.

The gist of the idea is you would write _pure_ functions like this with an imperative style (example in Haskell)

    bullet = do
      player <- getEntityByID playerID
      applyDamage 20 player
This will require some syntactic sugar (do notation in Haskell, for comprehension in Scala) to make it look procedure / imperative. However, the whole expression remains pure, and does not read or write to global state, it's just a data structure capable of expressing steps of mutations (and/or side effects).

A separate part of the system (interpreter) will actually collect all such data structures and apply actual updates to global states in one place and in a principled way.

Some additional benefits includes easily implementing different interpreters (eg. for unit testing), adding "middlewares" to support things like logging and type safety on mutations.

I have no idea whether game programmers uses this pattern or not. However, it's very common in games to add a scripting language (LUA is a popular one) to express things like unit AI, dialog trees on top the underlying engine; which seems rather similar architecturally: adding a layer of indirection, use a expressive (just enough) DSL to encode the logic for mutations with only limited exposure to underlying global states.

A better resource that explores this idea can be found here: http://gameprogrammingpatterns.com/bytecode.html

yiransheng··on Leaving Python for JavaScript
ES6+ javaScript in absorbed many great features from python: destructuring vs. tuple unpacking, spread (...) vs *args argument unpacking and generators. Many things I enjoyed in python are now also idiomatic javaScript. Only thing missing is function decorators:

    @connect(...)
    function StateLessComponent(props) {

    }
yiransheng··on Chris Lattner on the Realm WWDC 2017 Swift Panel
Inheritance based OOP models tree-like entites well, where hirachy is defined and clear cut. Unfortunately, lots of real life domains are best expressed by graphs - commonly a DAG. You need to pay attention to your edges and not just the nodes. Inheritance based OOP gives you one keyword to express your edges: extend, and it's horribly inadequate. Mutatable state is not a issue in Java OOP, lacking the expressive power is.
yiransheng··on NYPD is canceling its Palantir contract
Also plottable, one of the few large typescript open source libraries you can depend upon.
yiransheng··on A better way to represent loading state in ReactJS/Redux apps
With algebraic data type like in Haskell, the state could be typed like this:

    data State a = 
      Loading
      | Failed
      | Loaded [a]
Compared to a representation like `{ loading:true, error: false, pics:[] }`, the above model has the benefit of "making illegal state not representable".

One library in js I like to use to model this type of tagged union / ADT values is single-key[1]. Each variant in a tagged union type is represented by an plain object with just one key, for example:

    const state1 = { Loading: null };
    const state2 = { Failed : <reason> };
    const state3 = { Loaded : [...pics] };

The library offers pattern matching syntax on top of this data representation:

    // render
    const reactElement = match(state, {
      Loading : () => <Loading />,
      Failed  : (reason) => <Error {...reason}/>,
      Loaded  : (pics) => <PicsList pics={pics} />
    });

    // reducer
    const reducer = (state, action) => match(state, {
        Loading : () => {
          switch (action.type) {
            case PICS_FAILURE:
              return { Failure: action.payload };
            case PICS_SUCCESS:
              return { Loaded : action.payload };
            default:
              return state
          }
        },
        Failed : () => {
          switch (action.type) {
            case PICS_REQUEST:
              return { Loading: true };
            default:
              return state;
          }
        },
        Loaded : () => {
          switch (action.type) {
            case PICS_REQUEST:
              return { Loading: true };
            default:
              return state;
          }
        }
      });

    
[1] https://www.npmjs.com/package/single-key
yiransheng··on Why Elm? [pdf]
Calmm-js is one of these: https://github.com/calmm-js
yiransheng··on Statistics: Losing Ground to CS, Losing Image Among Students
In my opinion, one big reason in the fall of statistics is the asymmetry in curriculum.

If you are in CS/Machine Learning, you will be forced/suggested to take a few statistics/probability courses, typically up to a regression course. These are good enough to get you started and familiar with statistical concepts and methodology.

However, if you come from a typical statistics program, programming/computing courses are almost always never required. Professors in probability theory/statistics often do not have great computing skill, they might know enough to get around in R/Mathlab, and teach you the basics in these tools, but a lot of times you are left in the dark in terms learning how to code.

If your career goal is an analyst, whose job is report oriented, you can get by by knowing just enough R (SAS, Matlab etc.) scripting. As your deliverables are generally fancy LaTex pdf (or presentations). But current job market pays and values more of a "data scientist", whatever that terms mean. This type of roles typically requires solid software engineering background, and requires you to be able to work with software developers and commit to the same codebase on a regular basis. The lack of programming/computing skills of a stats majored students will be a huge minus for this type of responsibilities.

yiransheng··on Minecraft vanilla 4KB HDD, with read/write heads
As a matter of fact, with some external tools like MCEdit, it's not really as time consuming as it seems. A lot of large scale of Minecraft redstone contraptions are modular in nature, once you build and validate the basic idea, scaling up is just a matter of copy-pasting.

In my experience, the initial research/designing phase takes quite some hours (typically a good weekend), scaling up is must faster. In addition, most players who are into this typically have a number of creative worlds serve as libraries of past builds, so you'd almost never start from scratch.

yiransheng··on The lessons of Bulletstorm and the problem with price-points
Unfortunately engagement value is only weakly linked to the price tag. It's expected a AAA title costing $60 to give at least 70 hours of entertainment, but beyond that it varies vastly across titles and players.

Usually, games with multiplayer features last significantly longer than single player games, but there are exceptions like the ones you listed which all have high replay values. Also, genres matter, open-world sandbox games lack the intensity but enjoys longevity, the price could hardly reflect that.

Then there's Minecraft, which keeps its charm after months, for some people even years. And it cost a lot less than the typical AAA titles.

I feel the pricing model in gaming industry is so rigid and backwards (although no so much for casual games, eg. in app purchase and mulitplayer games eg. esport games and MMORPGS). Some movement toward more flexible pricing mechanisms to reflect games' contents will undoubted benefit both game makers and players.

yiransheng··on Good Poker Players Aren't Lucky
Also, if you are comfortable taking that amount of risk, you'd be better off to participate in the future market than buying large chunks of stocks on margin, at least you have extra liquidity.
yiransheng··on Turing Equivalent vs. Turing Complete
The comments in this post are also very informative.
yiransheng··on Haskell tutorial for C programmers
This tutorial itself has a zip file of non-trivial examples, if you scroll down to the last section.
yiransheng··on Why Python is Slow: Looking Under the Hood
Very well put, I am learning haskell at the moment, and beginning to appreciate the power of its type system in a way never which I never felt in Java, C etc.

When you chain a series of functions/actions together, only to supply data for computation last, type signatures almost always ensures the correctness of the entire computation. This is true even the computation occurs at a high abstraction level. It's almost like magic, when dealing with the more difficult concepts (well for beginners) like monads. There has been a number of times when I wrote something but cannot make sure it does what I want, yet it compiles and a few tests reveals it indeed works like a charm.

Whereas in dynamic languages, it'd be a nightmare to dig into the dirty details at runtime. I always have this problem in R, a series of supposedly elegantly linked operations fail for mistakes like forgetting to convert character values into factor type. To debug such errors, I always have to explicitly carry out middle steps and store intermediary results, until finally locating the problem.

Another note, even in javaScript, sometimes there at patterns encourage the use of implicit types or interfaces(typeclass in haskell). The canonical example is jQuery, most of the times you can safely do:

    $(something).method1().method2(param).method3()...
This is because any jQuery object inherits methods from $.fn, and most of these methods returns either the same jQuery object, or another jQuery object. In a sense, jQuery itself is a typeclass, anything looks like $(something) is a instance type.
yiransheng··on A Chinese copy of GitHub.com
Another relevant chart from this very site: https://code.csdn.net/explore/projects
yiransheng··on A Chinese copy of GitHub.com
CSDN is a tech forum/news site, kinda like Techcrunch. It has been around for quite a few years. This github like thingi seems like a joint effort between CSDN and Tencent, whose open source projects (currently six of them) can be found at https://code.csdn.net/Tencent.

Traditionally, China doesn't have a strong open source culture and Github was being blocked every now and then. Hopefully a Github clone backed by big players can change that.

yiransheng··on “var functionName” vs “function functionName”
An example case where "var" method is useful:

    function make_dot_product(dim) {

      if (dim === 3) {
         var dot = function(a,b) {
            return a.x*b.x+a.y*b.y+a.z*b.z
         }
      } else if (dim === 2) {
         var dot = function(a,b) {
            return a.x*b.x+a.y*b.y
         }
      }
  
      return dot
    }
vs.

    function make_dot_product(dim) {

      if (dim === 3) {
         function dot(a,b) {
            return a.x*b.x+a.y*b.y+a.z*b.z
         }
      } else if (dim === 2) {
         function dot(a,b) {
            return a.x*b.x+a.y*b.y
         }
      }
  
      return dot
    }
The second case is erroneous, as it will always return 2-dimensional dot function (even if you call make_dot_product(20)). The is because function nameSpace() is defined at parse time, so inside the make_dot_product scope, two instance of dot functions are created, with the second overwriting the first. If we use var f = function() {...} this can be avoided.
Page 1 of 2Next →