Opinion: parents around function arguments should not be optional

Hi Folks
Another bikeshed thought.
Having these parens be optional makes an opening for

Did you mean to annotate the parameter type or the return type?

but gives so little back.

If we’re into character optimizing how about we rename mapWithDefault :stuck_out_tongue:

Thanks
A

[edit: to say more I am trying to train a team of less experienced engineers and, as you may guess, they are very easily thrown by error messages, so I am really starting to appreciate the effort Elm made, etc]

2 Likes

How is this error getting triggered? Is it something like, starting with e.g.

let f = x => x + 1

And then trying to refactor it to:

let f = x, y => x + y

Example: let f = a: int => a + 1.
Did you mean to annotate the parameter type or the return type?

  1. (a): int => a + 1
  2. (a: int) => a + 1

https://rescript-lang.org/try?code=DYUwLgBAZhC8EEMBcECWA7SsB8iIGoIBGIA

1 Like

Got it, thank you. Small nitpick, but the wording of the error message and the examples given are in opposite orders.

EDIT: also, my 2c: not giving explicit type annotations at the implementation level also neatly avoids this issue (and brings other benefits as well). If type annotations are needed, interfaces are the best place to put them imho.

// Mod.resi
let f: int => int

// Mod.res
let f = a => a + 1

I think in general you define functions before applying them, which makes type annotation necessary for things like callbacks, function props in many cases. If I wanted to fix this with idiom Id be writing python :rofl:

I dont think a language like this should discourage people from annotating any time they feel the need as its valid, and ime is a common method of support for people new to typed systems like this.

Can you give an example? In my experience type annotations are rarely necessary. Perhaps I’m not understanding what you mean.

I dont think a language like this should discourage people from annotating any time they feel the need as its valid

This is not to discourage anyone, but just point to a more idiomatic way. Various times I’ve seen people struggle with type annotations working in slightly surprising ways.

ime is a common method of support for people new to typed systems like this.

Due to that and other benefits, I routinely advise putting type annotations in interface files, which are specially used for that purpose.

2 Likes

I’ve found that if you’re having to add a lot of explicit type annotations then you’re probably trying to do something un-idiomatic. I think it could be stressed more in the docs that annotating types is a code smell and if you can write code without annotations then other humans will be able to more easily understand it.

Sorry if this is slightly off topic :laughing:

1 Like
let fn = (x) => x.id

onClick={() => fn(x)}

(x has concrete type at onClick.)

This is a record access, so x has a concrete type here as well. The type checker needs to know which record type the field is coming from. E.g. if the record is defined in some other module:

// Item.res

type t = {id: int}

Then you can do:

let fn = x => x.Item.id

To tell it that the id field is the one defined in the Item module.

Sure but your argument was that you dont need to specify types and theres the example where you do.

In my defense I would say it’s not a type annotation but rather a module prefix, but fair enough :slight_smile:

I tend to agree with you we should be more consistent here instead of making it optional

2 Likes

I think that we use too many parents in rescript.
I like Elm & Haskell style with that. It’s more readable for me.
When symbols is many – then they become distracting when reading the code.

Anyway, I think that one way is better than two :slight_smile:

1 Like

Would it make more sense to make the parens required only if the function signature is annotated and keep the current behavior otherwise?

// OK
myArray->Array.map(item => item.id)

// Warning/Error
myArray->Array.map(item: MyItem.t => item.id)

Thats the existing behavior, sure.

Another thought is that the compiler could try both and only warn if both fail?

A