mirror of
https://github.com/tweag/nickel.git
synced 2024-09-20 16:08:14 +03:00
Update type vs contracts guide
This commit is contained in:
parent
ce233e725e
commit
80c3335447
@ -1,37 +1,37 @@
|
|||||||
# Type vs contracts: when to?
|
# Type vs contracts: when to?
|
||||||
|
|
||||||
You are writing Nickel code and wonder how it should be annotated. Leave it
|
You are writing Nickel code and wonder how it should be annotated. Leave it
|
||||||
without annotation ? Add type annotations? Use contracts? Here is a quick guide
|
alone? Add type annotations? Use contracts? Here is a quick guide when you don't
|
||||||
when you don't know what to do!
|
know what to do!
|
||||||
|
|
||||||
If your expression is:
|
What is the nature of the expression you are considering?
|
||||||
|
|
||||||
## A function
|
## A function
|
||||||
|
|
||||||
Most of the time, you should use **type annotations** for functions. Typing is
|
Most of the time, you should use **type annotations** for functions. For
|
||||||
most often more adapted than contracts for functions. You can exceptionally
|
reusable code, typing is often more adapted than contracts. You can
|
||||||
use contracts when types are not expressive enough to encode the property you
|
exceptionally use contracts when types are not expressive enough to encode the
|
||||||
want (as in `#ValidUrl -> #Port -> #ValidUrl`) or if the type system is not
|
property you want (such as in `#ValidUrl -> #Port -> #ValidUrl`) or if the type
|
||||||
powerful enough to see that the code is fine.
|
system is not powerful enough to see that your code is correct.
|
||||||
|
|
||||||
Type annotations do have a runtime cost. If you hit contracts-related
|
Type annotations do have a runtime cost. If you hit contracts-related
|
||||||
performances issues, you can always disable some contract checks using specific
|
performances issues, you can always disable some contract checks using specific
|
||||||
flags. With annotations, code is still typechecked, and you can still turn
|
flags (TO BE PRECISED ONCE WE HAVE THOSE FLAGS). With annotations, code is still
|
||||||
contracts checking back on for debugging mean when a problem arises. Without
|
typechecked, and you can turn contracts checking back on for debugging. Without
|
||||||
annotations, you're out of luck.
|
annotations, you're out of luck.
|
||||||
|
|
||||||
What to do depends on the context:
|
What to do depends on the context:
|
||||||
|
|
||||||
- *Anonymous function: nothing*. Short functions outsife of a let-binding can
|
- *Anonymous function: nothing*. Short, anonymous functions can
|
||||||
usually live without annotation. Inside a typed block, they will be
|
usually live without annotation. Inside a typed block, they will be
|
||||||
typechecked anyway. Outside, they won't be reused elsewhere, and are
|
typechecked anyway. Outside, anonymous function can't be reused elsewhere,
|
||||||
generally small functions passed as arguments to others typed higher-order
|
and are generally short functions passed as arguments to typed higher-order
|
||||||
functions, that will apply a guarding contract.
|
functions, which will apply a guarding contract.
|
||||||
|
|
||||||
Example: `lists.map (fun x => x + 1) [1,2,3]`
|
Example: `lists.map (fun x => x + 1) [1,2,3]`
|
||||||
- *Let-bound function outside of typed block: use a type annotation.* Even if
|
- *Let-bound function outside of typed block: use a type annotation.* Even if
|
||||||
local to a file, if your function is bound to a variable, it means it can
|
local to a file, if your function is bound to a variable, it can be
|
||||||
be potentially used in several places.
|
potentially reused in different places.
|
||||||
|
|
||||||
Example: `let appendTm: Str -> Str = fun s => s ++ "(TM)" in ...`
|
Example: `let appendTm: Str -> Str = fun s => s ++ "(TM)" in ...`
|
||||||
- *Let-bound function inside a typed block: nothing or type annotation*. Inside a
|
- *Let-bound function inside a typed block: nothing or type annotation*. Inside a
|
||||||
@ -46,16 +46,23 @@ What to do depends on the context:
|
|||||||
let foo : Num =
|
let foo : Num =
|
||||||
let addTwo = fun x => x + 2 in
|
let addTwo = fun x => x + 2 in
|
||||||
addTwo 4
|
addTwo 4
|
||||||
|
in ...
|
||||||
|
|
||||||
|
let foo : Num =
|
||||||
|
let ev : ((Num -> Num) -> Num) -> Num -> Num
|
||||||
|
= fun f x => f (functions.const x) in
|
||||||
|
ev (fun f => f 0) 1
|
||||||
|
in ...
|
||||||
```
|
```
|
||||||
|
|
||||||
## Library (record of functions)
|
## Library (record of functions)
|
||||||
|
|
||||||
You should use **type annoations** for records of functions. Currently Nickel
|
You should use **type annoations** for records of functions. Currently Nickel
|
||||||
doesn't have a specific notion of a library or a module. You can just put
|
doesn't have a specific notion of a library or a module. You can just put
|
||||||
functions inside a record. Given the function case, you should also use a type
|
functions inside a record. In accordance with the previous section, you should
|
||||||
annotation on your record to make the type of the functions accessible to the
|
also use a type annotation on your record to make the type of the functions
|
||||||
outside. Otherwise, the record is typed as `Dyn` and will shadow the type
|
accessible to the outside. Otherwise, the record is typed as `Dyn` and will
|
||||||
information, making your library basically unusable inside typed code.
|
obliterate the types, making your library basically unusable inside typed code.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@ -78,11 +85,12 @@ BUT DO
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, you can repeat your types both at the function level
|
Alternatively, you can repeat your types both at the function level and at the
|
||||||
and at the record level. It makes code more navigable and `query`-friendly, but at the expense of
|
record level. It makes code more navigable and `query`-friendly, but at the
|
||||||
repetition and duplicated contract checks. It is also currently required for
|
expense of repetition and duplicated contract checks. It is also currently
|
||||||
polymorphic functions because of [the following bug](). A better solution will probably be
|
required for polymorphic functions because of [the following
|
||||||
implemented in the future: type holes.
|
bug](https://github.com/tweag/nickel/issues/360). A better solution will
|
||||||
|
probably be implemented in the future: type holes (TODO: MAY ACTUALLY BE AVAILABLE FOR RELEASE)
|
||||||
|
|
||||||
(NOT YET POSSIBLE)
|
(NOT YET POSSIBLE)
|
||||||
```
|
```
|
||||||
@ -95,8 +103,8 @@ implemented in the future: type holes.
|
|||||||
## Data (record, list)
|
## Data (record, list)
|
||||||
|
|
||||||
Conversely, for data inside configuration code, you should use **contracts**.
|
Conversely, for data inside configuration code, you should use **contracts**.
|
||||||
Types are not adding much over contracts for configuration data, while contracts
|
Types are not adding much for configuration data, while contracts are more
|
||||||
are more flexible and expressive.
|
flexible and expressive.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```nickel
|
```nickel
|
||||||
@ -115,7 +123,7 @@ let Schema = {
|
|||||||
build = [
|
build = [
|
||||||
command "gcc hello.c -o hello",
|
command "gcc hello.c -o hello",
|
||||||
command "mv hello $out"
|
command "mv hello $out"
|
||||||
]
|
],
|
||||||
} | #Schema
|
} | #Schema
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -137,5 +145,4 @@ Usually, you should do **nothing**.
|
|||||||
|
|
||||||
At last, both type annotations and contracts come in handy for debugging. In
|
At last, both type annotations and contracts come in handy for debugging. In
|
||||||
this case, you don't have to follow the previous advices, and you can drop
|
this case, you don't have to follow the previous advices, and you can drop
|
||||||
random annotations or contract applications pretty much everywhere you see fit
|
random annotations or contract applications pretty much everywhere you see fit.
|
||||||
to help better locate when something is going wrong.
|
|
||||||
|
Loading…
Reference in New Issue
Block a user