ericelliott / rtype

Intuitive structural type notation for JavaScript.
MIT License
1.13k stars 38 forks source link

Idea: use arrows #16

Closed tomek-he-him closed 8 years ago

tomek-he-him commented 8 years ago

Hi guys,

now that I’ve used rtype for a couple of days there’s one thing which disrupts the overall feeling of awesomeness. It’s the (<args>): <value>: <Type> notation for callbacks. Here’s why.

1

I’ve grown used to reading : as a character which separates the value from the type. Consider this:

myFunction(
  a: String,
  b: [x: Boolean, y: Boolean, z: Boolean],
  c: (x: Array, y: Array, z: Array): Void,
  d: {x: Number, y: Number, z: Number},
) : // ...

When I quickly scan through this signature, I read the third parameter as some kind of a tuple. (Even though I’ve read the rtype spec a couple of times.) I write JS day to day – but this might be very confusing to someone coming from Python or Haskell. Especially since myFunction(a, b, (x, y, z), d) is totally valid JS syntax.

2

While this reads OK:

myFunction(string: Number): Function

This may be confusing for someone who doesn’t know the spec thoroughly:

myFunction(string: Number): boolean: Function

3

A real-world example (https://github.com/tomekwi/rfx/blob/164a4d37f9beacca5e8725d648910dd423ff4b7b/README.md#rfx-1).

rfx({
  type?: Rtype,
  name?: String,
  description?: String,
  doc?: String,
  example?: String,
  onError?: (error: TypeError): Void,
  ...metadata?: Object,
  fn: Function
}): Function

– knowing the rtype spec, I’d still need a couple of seconds to understand what onError should be. Imagine someone who doesn’t know the spec.

And it might get worse:

rfx({ onError?: (error: TypeError): Void, ...metadata?: Object }): Function

An alternative

Before I learned about rtype I used a similar spec called jsig. It’s not as good as rtype – there’s no next step that I know of (working parser or type checker).

They separated function arguments from the type with the fat arrow (=>). I think it’s easier to parse for a machine and easier to scan for a human than a colon – which has two different semantics in rtype at the moment.

Just let me show how the examples would look like if a fat arrow was at play:

1

myFunction(
  a: String,
  b: [x: Boolean, y: Boolean, z: Boolean],
  c: (x: Array, y: Array, z: Array) => Void,
  d: {x: Number, y: Number, z: Number},
) : // ...

2

myFunction: (string: Number) => boolean: Function

3

rfx({
  type?: Rtype,
  name?: String,
  description?: String,
  doc?: String,
  example?: String,
  onError?: (error: TypeError) => Void,
  ...metadata?: Object,
  fn: Function
}): Function
rfx({ onError?: (error: TypeError) => Void, ...metadata?: Object }): Function

Perhaps it’s a matter of habits that the => option reads better to me than the :. I might be wrong that => is easier to parse because of explicit semantics. Do let me know what you think!

tomek-he-him commented 8 years ago

Oh, and you get better syntax highlighting too. Some highlighters recognize JS functions. Here’s Atom with the font https://github.com/i-tu/Hasklig:

image

vs.

image

ericelliott commented 8 years ago

I'm open to => (that's how I started out), but I'm not so sure about nesting type definitions.

Nesting types is harder to read, may require more complexity (read: slower), and I don't see much in the way of benefit.

The rfx signature could be written:

onError(error: TypeError) => Void

rfx({
  type?: Rtype,
  name?: String,
  doc?: String,
  summary?: String,
  description?: String,
  example?: String,
  onError?: onError,
  ...metadata?: Object,
  fn: Function
}) => Function
ericelliott commented 8 years ago

Actually, I'm being silly. Using the arrow, nesting becomes a lot less complicated.

:+1: to arrows & nesting. Who wants to do the PR?

ericelliott commented 8 years ago

Done.

tomek-he-him commented 8 years ago

:+1: Great news!

tomek-he-him commented 8 years ago

Thanks!