-
Notifications
You must be signed in to change notification settings - Fork 377
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
Conversation
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]`. |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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. :)
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 😄
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
=>
expresses constraints on type variables.
More info / example for total newcomers here.->
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`. |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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").
There was a problem hiding this comment.
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. ;)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 aMaybe Type
.Array
, given a type gives back anArray Type
not just aType
.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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']]
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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".
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. |
No need for such drastic action! 😜 The nice thing about working on a feature branch is that one's free to $ git push origin issue-222 --force Editing the last commit is quite easy. Simply $ git commit --amend To edit commit $ git rebase abcd123^ --interactive Feel free to send me a message on Gitter if you have Git-related questions, @justin-calleja. |
07bdc8c
to
fa37368
Compare
@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 😎 |
@safareli I just noticed I never updated the example to use 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 | ||
``` |
There was a problem hiding this comment.
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`. |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davidchambers I've re-written the section based on this info and some research: https://github.com/justin-calleja/fantasy-land/tree/issue-222#type-signature-notation
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. |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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"._ |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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. 😜
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
is42
and we claim that it qualifies for membership in both theInteger
andNumber
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). |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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").
There was a problem hiding this comment.
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 (`, `)
. ;)
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
@davidchambers made the requested changes apart from the:
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). |
That would be nice, but we can leave that until the end.
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. |
There was a problem hiding this comment.
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.
Thank you for sticking with this, Justin & David! It's going to be a very valuable patch :) |
@justin-calleja, please squash your commits and |
@davidchambers thanks! |
Proposed fix for issue #222