HNHacker News
TopNewBestAskShowJobs

frankpf

634 karma · joined April 16, 2015

https://frankpf.com

Feel free to reach out to me: me@frankpf.com

submissionscomments
frankpf··on Announcing TypeScript 3.0
I like TypeScript's syntax, but I think the way function types are declared is ugly.

If you have a function from A to B, you have to declare its type as

    type MyFunction = (arg1: A) => B
instead of

    type MyFunction = A => B
Here's that syntax applied to the example from GP:

    function call<TS extends any[], R>(fn: (...args: TS) => R, ...args: TS): R { return fn(...args); }
vs.

    function call<TS extends any[], R>(fn: ...TS => R, ...args: TS): R { return fn(...args); }
frankpf··on Announcing Flutter Release Preview 1
TypeScript has ADTs, non-nullable types and the tooling is one of the best (great autocomplete, support for refactorings, incremental compilation, yarn is a great package manager, etc).

Its type system is also one of the most advanced in mainstream languages, being inferior only to Scala and Haskell.

In comparison, Dart doesn't have ADTs or non-nullable types. For a new language, I consider this very underwhelming.

I'm considering using Flutter for a new project because it seems to be a great platform, but having to use Dart instead of TypeScript is a step backwards.

frankpf··on TypeScript 2.9 is now available
I don't think TypeScript follows semver. TypeScript 2.9 is not compatible with 2.8.
frankpf··on Types, and Why You Should Care [video]
I am using `Named` as an argument. In structural type systems, what matters is the type structure. It's not duck typing because it's checked at compile-time.

If I change the type of "Named" to have the fields `firstName` and `lastName` of type string, accessing any other property inside `f` (like `obj.nonExisting`) or passing objects that don't have those fields.

frankpf··on Types, and Why You Should Care [video]
What do you mean by redefining? I'm not redefining the type of "name". If you want to get compile-time checking that `Person` implements `Named`, you can use `class Person implements Named`.

Even better than that, you don't need to use classes, you can use "normal" JS data structures. Extending the last example:

     const john = {
         name: 'John',
         age: 25
     } // No class involved

     f(john) // compiles

     const alice = {
         age: 25
         firstName: 'Alice',
         lastName: 'Jones'
     }

     f(alice) // compile-time error

Clojure.spec is cool, but I don't see how that is incompatible with static typing. You can still have libraries that check more complex properties at runtime.

EDIT: > Also, Clojure.spec allows you to be much more precise about properties. For example, it must be [...] non-nil [...]

TypeScript also handles nulls in the type system:

    function f(x: string | null) {
        if (x != null) {
              // tsc knows that inside this if, x can't be null
              return x.length 
        } else {
              console.log(x.length) // this doesn't compile, x is of type null here
        }
    }
frankpf··on Types, and Why You Should Care [video]
What you want to do is possible with a structural type system. In structural type systems, type compatibility is based on the structure of the type instead of the name (as opposed to nominal type systems).

An example in TypeScript:

     interface Named {
         name: string
     }
     
     class Person {
         name: string
         age: number
         // constructor here
     }
     
     function f(obj: Named) {
          // do something with obj.name
     }


     const joe = new Person('Joe', 25)
     
     // Compiles, even though Person has an extra `age` field
     // Person is structurally compatible with Named
     f(joe)
If you pass f() a class/object that doesn't have a `name` property of type string, the compiler would catch your error.
frankpf··on Exercises in Programming Style
IMO, having functions that accept different types (when the types are unrelated) is an anti-pattern in most cases, it doesn't matter if you make it more "maintainable" by using single dispatch.

It's fine when you have related types, but in the example `someFunction` accepts both ThisClass and str, which (I assume) have very different functionality.

You see this anti-pattern a lot in dynamically-typed languages. As an example, both these examples are valid and do the same thing in `knex`, an SQL query builder for node:

    knex.select('name', 'email').from('users')
    knex.select(['name', 'email']).from('users')
frankpf··on MongoDB gets support for multi-document ACID transactions
Can you clarify on those problems?
frankpf··on C as an intermediate language (2012)
> [...] pypy, a tracing jit for python2 and python3, is another project which gets compiled to C.

That's interesting. How does it work? I think most JIT compilers emit assembly directly and then execute it. Does PyPy generate C code while your program is running?

frankpf··on The scrypt parameters
Maybe you can show us this magic hardware that can crack millions of bcrypt hashes in seconds?

EDIT: Besides, bcrypt is not even the "best" hash function we have available. If you're concerned about GPUs and FPGAs, there's argon2id which has much stronger guarantees than bcrypt.

frankpf··on The scrypt parameters
> In the real-world, at scale the only relevant metric to his cost is the entropy of the password, which iterative hashing doesn't magically increase.

> If you find it cost-effective to slap a `for (i = 0; i < 10^12; i++)` loop around your hash, you should assume it's cost-effective for your attacker as well.

If your work factor is higher, it takes much longer to brute force a password. On my laptop, bcrypt with work factors 13 and 14 take ~650ms and ~1100ms, respectively. In comparison, SHA256 takes 0.110ms.

A database of passwords hashed with SHA256 will take days, maybe weeks to recover most of the passwords through brute force. With bcrypt, it would take months or years.

This article[1] has some numbers (although they're a bit outdated now):

> How much slower is bcrypt than, say, MD5? Depends on the work factor. Using a work factor of 12, bcrypt hashes the password yaaa in about 0.3 seconds on my laptop. MD5, on the other hand, takes less than a microsecond.

> So we’re talking about 5 or so orders of magnitude. Instead of cracking a password every 40 seconds, I’d be cracking them every 12 years or so. Your passwords might not need that kind of security and you might need a faster comparison algorithm, but bcrypt allows you to choose your balance of speed and security. Use it.

[1]: https://codahale.com/how-to-safely-store-a-password/

frankpf··on The scrypt parameters
Definitely do not use SHA256, it's designed to be fast. That's the opposite of what you want when storing passwords. I don't understand what's the problem with scrypt, but if you don't want to use it there's also bcrypt and argon2i.
frankpf··on Reflecting on Haskell in 2017
>Yes and no. Curried-by-default is the source of a lot of Haskell's power, and that requires a certain amount of unusual ordering. The terseness of pointfree style is worth some learning time.

Correct me if I'm wrong, but you don't need Haskell's syntax to have automatically curried functions or a pointfree style. Using a JS-like syntax as an example:

    function add(a, b) {
        return a + b;
    }
    // functions could be automatically curried,
    // so you could do this:
    let add5 = add(5);
    
    add5(10) // => 15
    

    // "Normal" sum definition
    function sum(list) {
        return reduce(add, 0, list); 
    }

    // Pointfree version
    function sum() {
       // returns a function that takes
       // 1 argument (the list)
       return reduce(add, 0);
    }
frankpf··on Thread Pools in Nginx Boost Performance 9x (2015)
Can you elaborate on why cores / 2 is the "ideal" number for throughput and latency?
frankpf··on Implementing Algebraic Effects in C
Hey there, Reason is a great project! Thanks for working on it!

I actually tried to use it very recently in a new project at my workplace, but unfortunately I decided not to. I found some fundamental things were lacking.

I didn't find documentation about how to interact with npm libraries (e.g. how do I use a websockets library in Reason? Do I have to create my own bindings? If so, how?).

There also seemed to be nothing similar to async/await in Reason/Bucklescript yet and I couldn't find out how to use promises.

I'll definitely reevaluate it in the future. I think it is an awesome project with great potential (along with Bucklescript which is awesome too).

frankpf··on Implementing Algebraic Effects in C
A little off-topic, but the author of this paper (Daan Leijen) is also the author of Koka[1], a programming language with algebraic effects.

IMO, Koka (or something similar) has more potential to become a mainstream language than "traditional" FP languages (Haskell, OCaml, Idris, etc.).

Effects seem to be easier to understand than monads (at least on a superficial level) and more modular (I don't have a lot of experience with Haskell, so take that with a grain of salt).

Its syntax is also very close to C-like languages.

Taken from the Koka book[2]:

  fun square1(x : int) : total int {
    return x*x
  }

  fun square2(x : int) : io int {
    println( "a not so secret side-effect" )
    return x*x
  }
`square1` is a pure mathematical function, so its effect is `total`. `square2` has a side-effect because of `println`, so its effect is `io`. This means that `square2` can raise exceptions, not terminate, be non-deterministic, read and write to the heap, and do any input/output operations.

Note that Koka can infer effects, so these annotations are optional.

[1]: https://www.microsoft.com/en-us/research/project/koka/?from=...

[2]: https://koka-lang.github.io/koka/doc/kokaspec.html#sec-effec...

frankpf··on x86: Approaching 40 and still going strong
It also failed because, at least 6 years before Itanium was even released (2001), there were better, faster options available.

In 1995, the Pentium Pro featured out-of-order and speculative execution[1]. This has two main advantages over the VLIW architecture (Itanium in particular):

1) The processor can use runtime information (such as past history of branches) to speculatively execute hundreds of instructions before they are needed.

2) When a stall happens due to a cache miss, in some cases the CPU can continue to execute instructions while waiting for the stall to end. This isn't possible with VLIW because the compiler can't predict cache hits/misses, this information is only available at runtime.

These two advantages combined made Itanium much slower than the then available x86 processors.

[1]: Note that Intel didn't invent these ideas. OoO was already known in the 1970s, although it was applied to mainframe supercomputers. I'm just using Pentium Pro as an example of a similar microprocessor.

frankpf··on x86: Approaching 40 and still going strong
> Of course instruction parallelism should be in the hands of the compiler -- it has so much richer information from the source-code so it can do a better job.

This is not true. Yes, the compiler has a lot of useful information, but it does not have access to runtime information, which the CPU does.

A modern superscalar CPU with out-of-order execution can execute instructions speculatively hundreds of cycles before they are even used.

Instructions after conditional branches can be executed speculatively using branch prediction and loads can be executed before stores (even though they might access the same address!). If the processor later detects that the load was dependent on the store, it can "undo" that operation, without any of these effects ever being visible to the running program.

frankpf··on Facebook Announces React Fiber, a Rewrite of Its React Framework
Would you mind elaborating on some of those practices (specifically, the problems that made working on the first codebase abhorrent)?
frankpf··on Swift and the Legacy of Functional Programming
> Most languages only have higher-order procedures, not higher-order functions.

What do you mean by that?

frankpf··on Graal and Truffle could accelerate programming language design
QBE[1] seems to be what you're looking for. It "aims to be a pure C embeddable backend that provides 70% of the performance of advanced compilers in 10% of the code".

Previous discussion on HN: https://news.ycombinator.com/item?id=11555527

[1]: http://c9x.me/compile/

frankpf··on A JVM Does That? (2011) [pdf]
I'm probably missing something obvious here, but on page 25 it says:

  Azul Systems has been busy rewriting lots of it
  - Many major subsystems are simpler, faster, lighter
  - >100k diffs from OpenJDK
If Azul Systems' JVM is based on OpenJDK, shouldn't it be open source? OpenJDK is licensed under GPLv2.
frankpf··on Poll: What Programming Language Do You Use for Server Side Web Development?
Check out Ur/Web[1].

It's a functional, pure and statically typed language inspired by Haskell. It claims that programs that compile successfully cannot:

- Suffer from any kinds of code-injection attacks

- Return invalid HTML

- Contain dead intra-application links

- Have mismatches between HTML forms and the fields expected by their handlers

- Include client-side code that makes incorrect assumptions about the "AJAX"-style services that the remote web server provides

- Attempt invalid SQL queries

- Use improper marshaling or unmarshaling in communication with SQL databases or between

[1]: http://www.impredicative.com/ur/

frankpf··on Idris, a language that will change the way you think about programming (2015)
I'm not an expert in Idris, but I will try to answer this as best as I can.

That's the Haskell type notation (Idris and Haskell are similar). Here's an example:

  plus5 :: Int -> Int
  plus5 n = n + 5
The first line is the type declaration of the `plus5` function. You can read it as "plus5 takes an Int as argument and returns another Int". The second line is the function declaration. The equivalent in Python would be:

  def plus5(n):
      return n + 5
The syntax is the same when functions have more arguments:

  add :: Int -> Int -> Int
  add a b = a + b
This means that add takes two integers as arguments and returns another integer. This syntax might look strange (you could be asking yourself "How do I distinguish between the argument and return types?") but that's because in Haskell functions are curried[1].

Haskell (and Idris as well) also has parametric polymorphism, so you can define functions which operate on generic types. For example, the identity function:

  id :: a -> a
  id x = x
You can read this as "`id` is a function that takes an argument of type a and returns another argument of type a (so the return type and argument type must be the same)". This means the id function will work for any type of argument (String, Float, Int, etc.). Note that of course this is not dynamic typing, Haskell and Idris are statically typed and therefore all the types are checked at compile-time.

Now, let's move on to Idris. One of the reasons there's a lot of excitement over Idris is because it has dependent types, i.e., you can declare types that depend on values and these are checked at compile-time.

The example GP gave was this:

  app : Vect n a -> Vect m a -> Vect (n + m) a
`app` here stands for append. This is the function that appends two vectors (lists of static length) together. In Idris, `Vect x y` is a vector of size x and type y, so

   Vect 5 Int
is a vector of 5 integers. So, essentially, `app` is a function that takes two vectors as arguments, `Vect n a` and `Vect m a` (this means they can be of different sizes but their content must be of the same type). It returns a `Vect (n + m) a`.

Think about it for a minute. That is amazing! We can be sure that, at compile-time, just by looking at the function signature, our append function, given two vectors of size n and m, will _always_ return a vector of size (n + m).

If we accidentally implement a function that does adhere to the type specification (for example, it appends the first argument to itself, returning a Vect of size (n + n) instead of (n + m)), the code will not compile and the compiler will tell us where is the bug.

[1]: http://learnyouahaskell.com/higher-order-functions#curried-f...

← PreviousPage 2 of 2