Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a section on "Type signature notation" in README.md #223

Merged
merged 1 commit into from
May 2, 2017

Conversation

justin-calleja
Copy link
Contributor

Proposed fix for issue #222

@davidchambers
Copy link
Member

Thanks, @justin-calleja! We should certainly include something like this.

README.md Outdated

The type signature notation used in this document is described [here](https://sanctuary.js.org/#types). In summary:

1. `->` (arrow) expresses a function's explicit parameter and return value types. `String` -> `[String]` -> `[String]` is a function which takes a `String` and an `[String]` and returns an `[String]`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haskell's special [] type constructor is not something we should emulate, in my view. In Sanctuary we've switched to using Array :: Type -> Type (as PureScript did quite a while ago), as it's more consistent:

maybeToArray :: Maybe a -> [a]
arrayToMaybe :: [a] -> Maybe a
maybeToArray :: Maybe a -> Array a
arrayToMaybe :: Array a -> Maybe a

Before introducing -> via String -> Array String -> Array String it would be good to introduce type constructors so readers know what Array String represents.

README.md Outdated
```
sequence :: Apply f, Traversable t => t (f a) ~> (b -> f b) -> f (t a)
'------' '--------------------' '-----' '-------------------'
'- method name '- constraints '- method target '- method arguments
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now require traverse rather than sequence. It would be good to update this. Also, the section labelled "method arguments" is actually argument type and return type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

traverse :: Applicative f, Traversable t => t a ~> (TypeRep f, a -> f b) -> f (t b)
'------'    '--------------------------'    '-'    '-------------------'    '-----'
 |           |                               |      |                        ^ result
 |           |                               |      ^ method arguments.
 |           |                               ^ method target (`this`)
 |           ^ constraints on type variables
 ^ method name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I changed the example to map's definition, because in traverse's:

traverse :: Applicative f, Traversable t => t a ~> (TypeRep f, a -> f b) -> f (t b)

I don't understand what TypeRep is. I know there's this section in the README (but I still don't get it):

## Type representatives

Certain behaviours are defined from the perspective of a member of a type.
Other behaviours do not require a member. Thus certain algebras require a
type to provide a value-level representative (with certain properties). The
Identity type, for example, could provide `Id` as its type representative:
`Id :: TypeRep Identity`.

If a type provides a type representative, each member of the type must have
a `constructor` property which is a reference to the type representative.

Maybe someone can add an example?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think traverse is good example as it contains multiple constraints and multiple arguments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@safareli , fair enough - I'll change it later and open another issue for an example of TypeRep.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justin-calleja this #180 (comment) thread might be relevant for understanding TypeRep.

README.md Outdated
4. `~>` (squiggly arrow) expresses a function's implicit parameter type. When a function is a property of a type, it is called a method. All methods have an implicit parameter type - the type of which they are a property of.
5. `=>` (fat arrow) expresses constraints on type variables. For example, `a -> b` is a function which takes an argument and returns a result. The argument can be any type. The result can be any type (including the same type as the argument). All this applies to the function described by `Functor f => f a -> f b` but we now require the type `f` to satisfy the Functor specification.

i.e.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change this line:

-i.e.
+For example:

README.md Outdated
' ' ' ' - argument type
' ' ' - method target type
' ' - type constraint
'- method name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would look better if type constraint and method name were aligned with argument type and method target type respectively. In other words, let's move these up two lines. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidchambers Is this good:

map :: Functor f => f a ~> (a -> b) -> f b
'-'    '-------'    '-'    '------'    '-'
 '      '            '      '           '
 '- method name      ' - method target type
        '                   '           '
        ' - type constraint ' - argument type
                                        '
                                        ' - return type

Or should "return type" be level 1? Also, what about space between type constraint and argument type? I kept it so small for consistency (all down arrows start from first "-" in the thing they underline).

README.md Outdated
2. Lowercase letters stand for type variables. Type variables can take any type unless they have been restricted by means of type constraints (see fat arrow below).
3. `->` (arrow) expresses a function's explicit parameter and return value types. `String` -> `Array String` -> `Array String` is a function which takes a `String` and an `Array String` and returns an `Array String`.
4. `~>` (squiggly arrow) expresses a function's implicit parameter type. When a function is a property of a type, it is called a method. All methods have an implicit parameter type - the type of which they are a property of.
5. `=>` (fat arrow) expresses constraints on type variables. For example, `a -> b` is a function which takes an argument and returns a result. The argument can be any type. The result can be any type (including the same type as the argument). All this applies to the function described by `Functor f => f a -> f b` but we now require the type `f` to satisfy the Functor specification.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wrap lines at 79 characters. Long lines make line comments less precise.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Semigroup a => a -> a -> a is a clearer example of a type-class constraint.

Copy link

@rstegg rstegg Mar 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this suggestion, and if I may make a suggestion? This is looking like a dictionary, so perhaps keeping concise and declarative definitions to a single sentence, for instance -

"=>: expresses constraints on type variables."

to make a reference point if the reader needs it [at least, IMO that is how the rest of the specs read - through reference points]

and staying concise/declarative can possibly add the illustration of what the signatures are used for

or otherwise, ignore this comment 😄

Copy link
Contributor Author

@justin-calleja justin-calleja Mar 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidchambers , I'm wrapping only this PR's content at 79 characters. Do you want another PR with the whole README.md wrapped at 79 chars?

Also, I changed pt 5 due to the new (Semigroup) example - see commit I'm pushing soon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rstegg true - for a "summary" it is getting a bit long. Basically, I want to target ppl who know next to nothing re the type signature notation. There is a reference to Sanctuary's docs - but I'd rather not have someone stay fishing for the relevant parts of the notation and just have it listed here.

I didn't see this comment originally as I would have made the changes with the last commit. I suggest the following:

  1. => expresses constraints on type variables.
    More info / example for total newcomers here.
  2. -> expresses a function's explicit parameter and return value types.
    More info / example for total newcomers here.

etc… (i.e. italic short description followed by new line + example / longer description)

There is now also the issue of tense in commit messages. @davidchambers please advise on what to do re that. Should I scrap the repo I'm making this PR with and create a new one with the current changes and @rstegg 's feedback?

README.md Outdated

The type signature notation used in this document is described [here](https://sanctuary.js.org/#types). In summary:

1. New types can be created via type constructors. Type constructors take one or more type arguments. For example, the type "array of strings" is written as `Array String`, and the type constructor `Array` can be defined as `Array :: a -> a`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type of Array is not a -> a but Type -> Type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidchambers Aren't lowercase letters used for type variables? What is Type? I would imagine you mean a type variable by it - but then it starts with capital case (so it could mean some defined type called "Type").

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There really is a Type type: sanctuary-js/sanctuary-def#125.

If we're talking the $.Array type constructor, it is not polymorphic – it must be applied to a Type value such as $.Integer. Array :: a -> a would be an oddly named identity function. ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks David. I'm still grokking this - I think I need to read up a bit more on type constructors (looking at Sanctuary).

I do get this though:

Array :: a -> a would be an oddly named identity function

so Array :: a -> a is wrong - but I'm still looking into why Array :: Type -> Type is right.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These ghci (Haskell) examples may be instructive:

> :kind String
String :: *

> :kind Maybe
Maybe :: * -> *

> :kind Maybe String
Maybe String :: *

One can think of * as the type of types. The type of String is *String is a type. The type of Maybe is * -> *Maybe is a type constructor.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe given a Type gives back a Maybe Type. Array, given a type gives back an Array Type not just a Type.

This is incorrect. An array of types looks like this:

[$.Boolean, $.Number, $.String] :: Array Type

$.Array($.Integer), on the other hand, represents a single type—Array Integer—rather than an array of types:

$.Array :: Type -> Type
$.Array($.Integer) :: Type

It's important to keep in mind that the type of $.Array($.Integer) is not Array Integer. Thinking about the types of types is especially confusing in a language without types, where types—and the types of types—can only exist at the value level.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidchambers I've updated this section in the README.md. Basically, my current understanding is there so feel free to slice and dice it as necessary 😄

I think I get it though. A lot hinges on properly understanding Type. I have defined it as the type of a "type expression" in the README.md (based on some looking around). Any thoughts on this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot hinges on properly understanding Type.

I think introducing a Type type actually makes things less clear. Another option is to provide a few examples to encourage an intuitive understanding rather than a theoretical one:

  • Array String is the type comprising every array of strings. Examples:

    • []
    • ['foo', 'bar', 'baz']
  • Array (Array String) is the type comprising every array of array of strings. Examples:

    • []
    • [[], [], [], []]
    • [[], ['foo'], ['bar', 'baz']]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidchambers Ok, so if I’m understanding you correctly, when explaining “type constructors”, I’ll remove the actual definitions and use examples only instead.

This means we can also remove the part “the value of an expression may be a type” - since the whole point of introducing it was because I needed it to use actual type definitions in the “type constructor” part.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's what I meant by my prev comment: justin-calleja@97da83c

README.md Outdated
1. New types can be created via type constructors. Type constructors take one or more type arguments. For example, the type "array of strings" is written as `Array String`, and the type constructor `Array` can be defined as `Array :: a -> a`.
2. Lowercase letters stand for type variables. Type variables can take any type unless they have been restricted by means of type constraints (see fat arrow below).
3. `->` (arrow) expresses a function's explicit parameter and return value types. `String` -> `Array String` -> `Array String` is a function which takes a `String` and an `Array String` and returns an `Array String`.
4. `~>` (squiggly arrow) expresses a function's implicit parameter type. When a function is a property of a type, it is called a method. All methods have an implicit parameter type - the type of which they are a property of.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could drop the last word of the last sentence to avoid the double "of".

@justin-calleja
Copy link
Contributor Author

btw, just noticed I'm using past tense in commit messages (whereas other commit messages use imperative tense).

I'm not sure how to change this now that I've pushed to my repo. I could delete the repo and add all these changes as one commit with imperative tense in another fork. I also have no problem with anyone just taking the changes and committing properly with their username - seriously, I just want the info on the README.md.

@davidchambers
Copy link
Member

I'm not sure how to change this now that I've pushed to my repo. I could delete the repo and add all these changes as one commit with imperative tense in another fork.

No need for such drastic action! 😜

The nice thing about working on a feature branch is that one's free to --force push:

$ git push origin issue-222 --force

Editing the last commit is quite easy. Simply git add some changes then run:

$ git commit --amend

To edit commit abcd123 one would run:

$ git rebase abcd123^ --interactive

Feel free to send me a message on Gitter if you have Git-related questions, @justin-calleja.

@justin-calleja
Copy link
Contributor Author

justin-calleja commented Mar 5, 2017

@davidchambers thanks! If only the force were always a flag away 😆

Squashed everything to a single commit - as they were all past tense. I thought I'd break comment history with that - guess not 😎

@justin-calleja
Copy link
Contributor Author

@safareli I just noticed I never updated the example to use traverse. I think it's because I never got what TypeRep is and I was going to do that after (in case I could add some more info).

In any case, updated it now. @davidchambers note that the example is over 79 chars long (otherwise it would break the format of the e.g. - some other lines in the README are also over 79 chars long).

' ' - type constraints ' ' - argument types ' - return type
' '
'- method name ' - method target type
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated formatting looks good!

README.md Outdated
1. _New types can be created via type constructors._
- Type constructors take one or more type arguments. For example, the type
"array of strings" is written as `Array String`, and the type constructor
`Array` can be defined as `Array :: Type -> Type`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this point should come last, or be removed. An explanation of the notation should start with the simplest possible example and build from there. Type constructors should not be introduced until the groundwork has been laid.

I suggest something like this:

-- simple example
true :: Boolean
-- show that a value can be a member of multiple types
42 :: Integer, Number
-- introduce type constructors
["foo", "bar", "baz"] :: Array String
-- introduce the (->) type constructor
Math.abs :: Number -> Number

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README.md Outdated
- For example, `a ~> a -> a` is a method on some type `a`, which takes an
argument and returns a result, both of which must be of the same type `a`.
All this applies to the method described by `Semigroup a => a ~> a -> a` but
we now require the type `a` to also satisfy the Semigroup specification.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this list should be a <ul> rather than an <ol>.

README.md Outdated
## Type signature notation

The type signature notation used in this document is described
[here](https://sanctuary.js.org/#types). In summary:
Copy link

@alexandradeas alexandradeas Mar 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is linking to the Sanctuary type defs adequate? If Fantasy land is intending to be a specification for API's, should it not define type defs itself rather than deferring to other libraries? Probably beyond the scope of this change, but curious to know what other people's thoughts are for defining a type syntax as part of FL

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alex-deas - personally, I'd link to anything I might think useful to people without prior knowledge of Haskell. The type signature notation is based on Haskell's - with (AFAIK) some extensions since we're annotating JS code (like the ~> for "method type constructor").

I am currently re-writing this PR and think I'll make the link to Sanctuary less prominent (i.e. footnote - "for more, see Link")

README.md Outdated
The type signature notation used in this document is described below:<sup
id="sanctuary-types-return">[1](#sanctuary-types)</sup>

* `::` _"has type of"._
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe "is a member of" is preferable to "has type of". The latter implies that a value's type is a property of the value. It's better to encourage readers to think of types as sets of values. Just as the values in the set {1,2,3} are oblivious to their membership of the set, true and false are oblivious to their membership of {true,false} (which we refer to as Boolean).

Using "has type of" also encourages readers to think in terms of typeof and instanceof, which—ignoring inheritance—encourage us to think of every value as belonging in exactly one set of values.

README.md Outdated

- - -
1. <a name="sanctuary-types"></a>See the [Types](https://sanctuary.js.org/#types)
section in `Sanctuary.js`'s docs for more info. [↩](#sanctuary-types-return)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sanctuary should be written without a file extension. 😜

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hehe sure - bad habits ^^;

README.md Outdated
* `::` _"is a member of"._
- `e :: t` can be read as: "the expression `e` is a member of type `t`".
- `true :: Boolean` - "`true` is a member of type `Boolean`".
- `42 :: Integer, Number` - "`42` is a member of type `Integer` or `Number`" (the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be and rather than or.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidchambers I also removed this part as I think it's extra:

(the value of the expression 42 is 42 and we claim that it qualifies for membership in both the Integer and Number types).

README.md Outdated
]`, `[ [], ['foo'], ['bar', 'baz'] ]`.
* _Lowercase letters stand for type variables._
- Type variables can take any type unless they have been restricted by means
of type constraints (see fat arrow below).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The indentation is off here.

README.md Outdated
function which accepts more than one argument (i.e. has arity greater
than 1). When used in this way, the syntax is: `(<input-types>) ->
<output-type>`, where <input-types> comprises two or more comma–space
`(, )` -separated type representations.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how to interpret these parentheses.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidchambers I got that from https://sanctuary.js.org/#types

(search for "separated").

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a subtle but significant difference between `(, )` and (`, `). ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh right ^^; nice catch!

README.md Outdated
`Array String`.
* `~>` (squiggly arrow) _Method type constructor._
- When a function is a property of an Object, it is called a method. All methods
have an implicit parameter type - the type of which they are a property.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The indentation is off here.

README.md Outdated
to implement any function / method defined by that typeclass. In
addition, any implementation of such function / method must also satisfy
any laws defined by a given typeclass for the function / method in
question.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest dropping the spaces around the slash (/) characters.

README.md Outdated

- - -
1. <a name="sanctuary-types"></a>See the [Types](https://sanctuary.js.org/#types)
section in `Sanctuary`'s docs for more info. [↩](#sanctuary-types-return)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: We needn't format Sanctuary specially.

@justin-calleja
Copy link
Contributor Author

@davidchambers made the requested changes apart from the:

(, ) -separated

one. Not sure what to change there.

btw, I assume you want these rebased to a single commit right? I take it that that doesn't break any of the history here (comments in this chat).

@davidchambers
Copy link
Member

I assume you want these rebased to a single commit right?

That would be nice, but we can leave that until the end.

I take it that that doesn't break any of the history here (comments in this chat).

Correct. The only comments that become detached from a pull request when rebasing are commit comments, which means it's usually correct to make line comments on the overall diff rather than on individual commits.

README.md Outdated
means of type constraints (see fat arrow below).
* `->` (arrow) _Function type constructor._
- `->` is an _infix_ type construcor that takes 2 type arguments and gives
the type of functions between these 2 type arguments.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general I prefer to write single-digit numbers as words. I feel particularly strongly about this in a programming context, as it helps to differentiate the JavaScript value 2 from the cardinal number two.

@rjmk
Copy link
Contributor

rjmk commented Mar 28, 2017

Thank you for sticking with this, Justin & David! It's going to be a very valuable patch :)

@davidchambers
Copy link
Member

@justin-calleja, please squash your commits and git rebase master. I'll then merge this. :)

@justin-calleja
Copy link
Contributor Author

@davidchambers thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants