mirror of
https://github.com/gren-lang/compiler.git
synced 2024-09-11 16:56:29 +03:00
Rename elm to gren in hint files.
This commit is contained in:
parent
ef89b431b8
commit
28d7d32dc3
@ -1,30 +1,29 @@
|
||||
|
||||
# Hints for Bad Recursion
|
||||
|
||||
There are two problems that will lead you here, both of them pretty tricky:
|
||||
|
||||
1. [**No Mutation**](#no-mutation) — Defining values in Elm is slightly different than defining values in languages like JavaScript.
|
||||
1. [**No Mutation**](#no-mutation) — Defining values in Gren is slightly different than defining values in languages like JavaScript.
|
||||
|
||||
2. [**Tricky Recursion**](#tricky-recursion) — Sometimes you need to define recursive values when creating generators, decoders, and parsers. A common case is a JSON decoder a discussion forums where a comment may have replies, which may have replies, which may have replies, etc.
|
||||
|
||||
|
||||
## No Mutation
|
||||
|
||||
Languages like JavaScript let you “reassign” variables. When you say `x = x + 1` it means: whatever `x` was pointing to, have it point to `x + 1` instead. This is called *mutating* a variable. All values are immutable in Elm, so reassigning variables does not make any sense! Okay, so what *should* `x = x + 1` mean in Elm?
|
||||
Languages like JavaScript let you “reassign” variables. When you say `x = x + 1` it means: whatever `x` was pointing to, have it point to `x + 1` instead. This is called *mutating* a variable. All values are immutable in Gren, so reassigning variables does not make any sense! Okay, so what *should* `x = x + 1` mean in Gren?
|
||||
|
||||
Well, what does it mean with functions? In Elm, we write recursive functions like this:
|
||||
Well, what does it mean with functions? In Gren, we write recursive functions like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
factorial : Int -> Int
|
||||
factorial n =
|
||||
if n <= 0 then 1 else n * factorial (n - 1)
|
||||
```
|
||||
|
||||
One cool thing about Elm is that whenever you see `factorial 3`, you can always replace that expression with `if 3 <= 0 then 1 else 3 * factorial (3 - 1)` and it will work exactly the same. So when Elm code gets evaluated, we will keep expanding `factorial` until the `if` produces a 1. At that point, we are done expanding and move on.
|
||||
One cool thing about Gren is that whenever you see `factorial 3`, you can always replace that expression with `if 3 <= 0 then 1 else 3 * factorial (3 - 1)` and it will work exactly the same. So when Gren code gets evaluated, we will keep expanding `factorial` until the `if` produces a 1. At that point, we are done expanding and move on.
|
||||
|
||||
The thing that surprises newcomers is that recursion works the same way with values too. So take the following definition:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
x = x + 1
|
||||
```
|
||||
|
||||
@ -32,18 +31,18 @@ We are actually defining `x` in terms of itself. So it would expand out to `x =
|
||||
|
||||
The fix is usually to just give the new value a new name. So you could rewrite it to:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
x1 = x + 1
|
||||
```
|
||||
|
||||
Now `x` is the old value and `x1` is the new value. Again, one cool thing about Elm is that whenever you see a `factorial 3` you can safely replace it with its definition. Well, the same is true of values. Wherever I see `x1`, I can replace it with `x + 1`. Thanks to the way definitions work in Elm, this is always safe!
|
||||
Now `x` is the old value and `x1` is the new value. Again, one cool thing about Gren is that whenever you see a `factorial 3` you can safely replace it with its definition. Well, the same is true of values. Wherever I see `x1`, I can replace it with `x + 1`. Thanks to the way definitions work in Gren, this is always safe!
|
||||
|
||||
|
||||
## Tricky Recursion
|
||||
|
||||
Now, there are some cases where you *do* want a recursive value. Say you are building a website with comments and replies. You may define a comment like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type alias Comment =
|
||||
{ message : String
|
||||
, upvotes : Int
|
||||
@ -57,7 +56,7 @@ type Responses =
|
||||
|
||||
You may have run into this definition in the [hints for recursive aliases](recursive-alias.md)! Anyway, once you have comments, you may want to turn them into JSON to send back to your server or to store in your database or whatever. So you will probably write some code like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
import Json.Decode as Decode exposing (Decoder)
|
||||
|
||||
decodeComment : Decoder Comment
|
||||
@ -78,7 +77,7 @@ The problem is that now `decodeComment` is defined in terms of itself! To know w
|
||||
|
||||
In this case, the trick is to use `Json.Decode.lazy` which delays the evaluation of a decoder until it is needed. So the valid definition would look like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
import Json.Decode as Decode exposing (Decoder)
|
||||
|
||||
decodeComment : Decoder Comment
|
||||
@ -95,7 +94,7 @@ decodeResponses =
|
||||
Decode.map Responses (Decode.list (Decode.lazy (\_ -> decodeComment)))
|
||||
```
|
||||
|
||||
Notice that in `decodeResponses`, we hide `decodeComment` behind an anonymous function. Elm cannot evaluate an anonymous function until it is given arguments, so it allows us to delay evaluation until it is needed. If there are no comments, we will not need to expand it!
|
||||
Notice that in `decodeResponses`, we hide `decodeComment` behind an anonymous function. Gren cannot evaluate an anonymous function until it is given arguments, so it allows us to delay evaluation until it is needed. If there are no comments, we will not need to expand it!
|
||||
|
||||
This saves us from expanding the value infinitely. Instead we only expand the value if we need to.
|
||||
|
||||
@ -106,9 +105,9 @@ This saves us from expanding the value infinitely. Instead we only expand the va
|
||||
|
||||
The compiler tries to detect bad recursion, but how does it know the difference between good and bad situations? Writing `factorial` is fine, but writing `x = x + 1` is not. One version of `decodeComment` was bad, but the other was fine. What is the rule?
|
||||
|
||||
**Elm will allow recursive definitions as long as there is at least one lambda before you get back to yourself.** So if we write `factorial` without any pretty syntax, it looks like this:
|
||||
**Gren will allow recursive definitions as long as there is at least one lambda before you get back to yourself.** So if we write `factorial` without any pretty syntax, it looks like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
factorial =
|
||||
\n -> if n <= 0 then 1 else n * factorial (n - 1)
|
||||
```
|
||||
@ -117,7 +116,7 @@ There is technically a lambda between the definition and the use, so it is okay!
|
||||
|
||||
**This rule is nice, but it does not catch everything.** It is pretty easy to write a definition where the recursion is hidden behind a lambda, but it still immediately expands forever:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
x =
|
||||
(\_ -> x) () + 1
|
||||
```
|
||||
|
@ -9,7 +9,7 @@ This page aims to catalog these scenarios and offer alternative paths that can g
|
||||
|
||||
It is common to try to get some extra type safety by creating really simple custom types:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type Id = Id Int
|
||||
type Age = Age Int
|
||||
|
||||
@ -19,11 +19,11 @@ type Description = Description String
|
||||
|
||||
By wrapping the primitive values like this, the type system can now help you make sure that you never mix up a `Id` and an `Age`. Those are different types! This trick is extra cool because it has no runtime cost in `--optimize` mode. The compiler can just use an `Int` or `String` directly when you use that flag!
|
||||
|
||||
The problem arises when you want to use a `Id` as a key in a dictionary. This is a totally reasonable thing to do, but the current version of Elm cannot handle this scenario.
|
||||
The problem arises when you want to use a `Id` as a key in a dictionary. This is a totally reasonable thing to do, but the current version of Gren cannot handle this scenario.
|
||||
|
||||
Instead of creating a `Dict Id Info` type, one thing you can do is create a custom data structure like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
module User exposing (Id, Table, empty, get, add)
|
||||
|
||||
import Dict exposing (Dict)
|
||||
@ -67,7 +67,7 @@ So while this approach is not as convenient as using a `Dict` directly, it has s
|
||||
|
||||
Say you need to define a `trafficLightToInt` function:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type TrafficLight = Green | Yellow | Red
|
||||
|
||||
trafficLightToInt : TrafficLight -> Int
|
||||
@ -79,7 +79,7 @@ We have heard that some people would prefer to use a dictionary for this sort of
|
||||
|
||||
I would recommend using a `case` expression though:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type TrafficLight = Green | Yellow | Red
|
||||
|
||||
trafficLightToInt : TrafficLight -> Int
|
||||
@ -95,4 +95,4 @@ This is really straight-forward while avoiding questions like “is `Green` less
|
||||
|
||||
## Something else?
|
||||
|
||||
If you have some other situation, please tell us about it [here](https://github.com/elm/error-message-catalog/issues). That is a log of error messages that can be improved, and we can use the particulars of your scenario to add more advice on this page!
|
||||
If you have some other situation, please tell us about it [here](https://github.com/gren/error-message-catalog/issues). That is a log of error messages that can be improved, and we can use the particulars of your scenario to add more advice on this page!
|
||||
|
@ -1,4 +1,3 @@
|
||||
# Comparing Records
|
||||
|
||||
The built-in comparison operators work on a fixed set of types, like `Int` and `String`. That covers a lot of cases, but what happens when you want to compare records?
|
||||
|
||||
@ -11,7 +10,7 @@ Say we want a `view` function that can show a list of students sorted by differe
|
||||
|
||||
We could create something like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
import Html exposing (..)
|
||||
|
||||
type alias Student =
|
||||
@ -38,9 +37,9 @@ viewStudent student =
|
||||
li [] [ text student.name ]
|
||||
```
|
||||
|
||||
If you are worried about the performance of changing the order or updating information about particular students, you can start using the [`Html.Lazy`](https://package.elm-lang.org/packages/elm/html/latest/Html-Lazy) and [`Html.Keyed`](https://package.elm-lang.org/packages/elm/html/latest/Html-Keyed) modules. The updated code would look something like this:
|
||||
If you are worried about the performance of changing the order or updating information about particular students, you can start using the [`Html.Lazy`](https://package.gren-lang.org/packages/gren/html/latest/Html-Lazy) and [`Html.Keyed`](https://package.gren-lang.org/packages/gren/html/latest/Html-Keyed) modules. The updated code would look something like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
import Html exposing (..)
|
||||
import Html.Lazy exposing (lazy)
|
||||
import Html.Keyed as Keyed
|
||||
@ -82,4 +81,4 @@ By using `Keyed.ul` we help the renderer move the DOM nodes around based on thei
|
||||
|
||||
## Something else?
|
||||
|
||||
If you have some other situation, please tell us about it [here](https://github.com/elm/error-message-catalog/issues). That is a log of error messages that can be improved, and we can use the particulars of your scenario to add more advice on this page!
|
||||
If you have some other situation, please tell us about it [here](https://github.com/gren/error-message-catalog/issues). That is a log of error messages that can be improved, and we can use the particulars of your scenario to add more advice on this page!
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
# Implicit Casts
|
||||
|
||||
Many languages automatically convert from `Int` to `Float` when they think it is necessary. This conversion is often called an [implicit cast](https://en.wikipedia.org/wiki/Type_conversion).
|
||||
@ -14,15 +13,15 @@ Languages that will add in implicit casts for addition include:
|
||||
- Java
|
||||
- Scala
|
||||
|
||||
These languages generally agree that an `Int` may be implicitly cast to a `Float` when necessary. So everyone is doing it, why not Elm?!
|
||||
These languages generally agree that an `Int` may be implicitly cast to a `Float` when necessary. So everyone is doing it, why not Gren?!
|
||||
|
||||
## Type Inference + Implicit Casts
|
||||
|
||||
Elm comes from the ML-family of languages. Languages in the ML-family that **never** do implicit casts include:
|
||||
Gren comes from the ML-family of languages. Languages in the ML-family that **never** do implicit casts include:
|
||||
|
||||
- Standard ML
|
||||
- OCaml
|
||||
- Elm
|
||||
- Gren
|
||||
- F#
|
||||
- Haskell
|
||||
|
||||
@ -32,7 +31,7 @@ Well, we have to go back to the 1970s for some background. J. Roger Hindley and
|
||||
|
||||
For decades, the problem was that nobody could figure out how to combine type inference with implicit casts AND make the resulting algorithm efficient enough for daily use. As far as I know, Scala was the first widely known language to figure out how to combine these two things! Its creator, Martin Odersky did a lot of work on combining type inference and subtyping to make this possible.
|
||||
|
||||
So for any ML-family language designed before Scala, it is safe to assume that implicit conversions just was not an option. Okay, but what about Elm?! It comes after Scala, so why not do it like them?!
|
||||
So for any ML-family language designed before Scala, it is safe to assume that implicit conversions just was not an option. Okay, but what about Gren?! It comes after Scala, so why not do it like them?!
|
||||
|
||||
1. You pay performance cost to mix type inference and implicit conversions. At least as far as anyone knows, it defeats an optimization that is crucial to getting _reliably_ good performance. It is fine in most cases, but it can be a real issue in very large code bases.
|
||||
|
||||
@ -45,6 +44,6 @@ This user data may be confounded by the fact that Scala allows quite extensive c
|
||||
|
||||
First, based on the landscape of design possibilities, it seems like requiring _explicit_ conversions is a pretty nice balance. We can have type inference, it can produce friendly error messages, the algorithm is snappy, and an unintended implicit cast will not flow hundreds of lines before manifesting to the user.
|
||||
|
||||
Second, Elm very much favors explicit code, so this also fits in with the overall spirit of the language and libraries.
|
||||
Second, Gren very much favors explicit code, so this also fits in with the overall spirit of the language and libraries.
|
||||
|
||||
I hope that clarifies why you have to add those `toFloat` and `round` functions! It definitely can take some getting used to, but there are tons of folks who get past that acclimation period and really love the tradeoffs!
|
||||
|
@ -1,9 +1,8 @@
|
||||
|
||||
# Import Cycles
|
||||
|
||||
What is an import cycle? In practice you may see it if you create two modules with interrelated `User` and `Comment` types like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
module Comment exposing (..)
|
||||
|
||||
import User
|
||||
@ -14,7 +13,7 @@ type alias Comment =
|
||||
}
|
||||
```
|
||||
|
||||
```elm
|
||||
```gren
|
||||
module User exposing (..)
|
||||
|
||||
import Comment
|
||||
@ -41,7 +40,7 @@ There are quite a few ways to break our `Comment` and `User` cycle from above, s
|
||||
|
||||
One approach is to just combine the two modules. If we check out the resulting code, we have actually revealed a problem in how we are representing our data:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
module BadCombination1 exposing (..)
|
||||
|
||||
type alias Comment =
|
||||
@ -57,7 +56,7 @@ type alias User =
|
||||
|
||||
Notice that the `Comment` type alias is defined in terms of the `User` type alias and vice versa. Having recursive type aliases like this does not work! That problem is described in depth [here](recursive-alias.md), but the quick takeaway is that one `type alias` needs to become a `type` to break the recursion. So let’s try again:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
module BadCombination2 exposing (..)
|
||||
|
||||
type alias Comment =
|
||||
@ -75,9 +74,9 @@ type AllUserComments = AllUserComments (List Comment)
|
||||
|
||||
Okay, now we have broken the recursion, but we need to ask ourselves, how are we going to actually instantiate these `Comment` and `User` types that we have described. A `Comment` will always have an author, and that `User` will always refer back to the `Comment`. So we seem to want cyclic data here. If we were in JavaScript we might instantiate all the comments in one pass, and then go back through and mutate the users to point to all the relevant comments. In other words, we need *mutation* to create this cyclic data!
|
||||
|
||||
All values are immutable in Elm, so we need to use a more functional strategy. One common approach is to use unique identifiers. Instead of referring directly to “the user object” we can refer to a user ID:
|
||||
All values are immutable in Gren, so we need to use a more functional strategy. One common approach is to use unique identifiers. Instead of referring directly to “the user object” we can refer to a user ID:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
module GoodCombination exposing (..)
|
||||
|
||||
import Dict
|
||||
@ -95,7 +94,7 @@ type alias AllComments =
|
||||
|
||||
Now in this world, we do not even have cycles in our types anymore! That means we can actually break these out into separate modules again:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
module Comment exposing (..)
|
||||
|
||||
import Dict
|
||||
@ -110,13 +109,13 @@ type alias AllComments =
|
||||
Dict.Dict User.Id (List Comment)
|
||||
```
|
||||
|
||||
```elm
|
||||
```gren
|
||||
module User exposing (..)
|
||||
|
||||
type alias Id = String
|
||||
```
|
||||
|
||||
So now we are back to the two modules we wanted, but we have data structures that are going to work much better in a functional language like Elm! **This is the common approach, and it is what you hope will happen!**
|
||||
So now we are back to the two modules we wanted, but we have data structures that are going to work much better in a functional language like Gren! **This is the common approach, and it is what you hope will happen!**
|
||||
|
||||
|
||||
## 2. Make a New Module
|
||||
@ -130,7 +129,7 @@ Now say there are actually a ton of functions and values in the `Comment` and `U
|
||||
|
||||
Another way to avoid module cycles is to be more generic in how you represent your data:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
module Comment exposing (..)
|
||||
|
||||
type alias Comment author =
|
||||
@ -139,7 +138,7 @@ type alias Comment author =
|
||||
}
|
||||
```
|
||||
|
||||
```elm
|
||||
```gren
|
||||
module User exposing (..)
|
||||
|
||||
type alias User comment =
|
||||
@ -155,13 +154,13 @@ So this strategy fails pretty badly with our particular example. The code is mor
|
||||
|
||||
## 4. Hiding Implementation Details in Packages
|
||||
|
||||
This gets a little bit trickier when you are creating a package like `elm-lang/parser` which is built around the `Parser` type.
|
||||
This gets a little bit trickier when you are creating a package like `gren-lang/parser` which is built around the `Parser` type.
|
||||
|
||||
That package has a couple exposed modules: `Parser`, `Parser.LanguageKit`, and `Parser.LowLevel`. All of these modules want access to the internal details of the `Parser` type, but we do not want to ever expose those internal details to the *users* of this package. So where should the `Parser` type live?!
|
||||
|
||||
Usually you know which module should expose the type for the best public API. In this case, it makes sense for it to live in the `Parser` module. The way to manage this is to create a `Parser.Internal` module with a definition like:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
module Parser.Internal exposing (..)
|
||||
|
||||
type Parser a =
|
||||
@ -170,7 +169,7 @@ type Parser a =
|
||||
|
||||
Now we can `import Parser.Internal` and use it in any of the modules in our package. The trick is that we never expose the `Parser.Internal` module to the *users* of our package. We can see what is inside, but they cannot! Then in the `Parser` module we can say:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
module Parser exposing (..)
|
||||
|
||||
import Parser.Internal as Internal
|
||||
|
@ -1,18 +1,17 @@
|
||||
|
||||
# Hints for Imports
|
||||
|
||||
When getting started with Elm, it is pretty common to have questions about how the `import` declarations work exactly. These questions usually arise when you start playing with the `Html` library so we will focus on that.
|
||||
When getting started with Gren, it is pretty common to have questions about how the `import` declarations work exactly. These questions usually arise when you start playing with the `Html` library so we will focus on that.
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
## `import`
|
||||
|
||||
An Elm file is called a **module**. To access code in other files, you need to `import` it!
|
||||
An Gren file is called a **module**. To access code in other files, you need to `import` it!
|
||||
|
||||
So say you want to use the [`div`](http://package.elm-lang.org/packages/elm-lang/html/latest/Html#div) function from the [`elm-lang/html`](http://package.elm-lang.org/packages/elm-lang/html/latest) package. The simplest way is to import it like this:
|
||||
So say you want to use the [`div`](http://package.gren-lang.org/packages/gren-lang/html/latest/Html#div) function from the [`gren-lang/html`](http://package.gren-lang.org/packages/gren-lang/html/latest) package. The simplest way is to import it like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
import Html
|
||||
|
||||
main =
|
||||
@ -22,11 +21,11 @@ main =
|
||||
After saying `import Html` we can refer to anything inside that module as long as it is *qualified*. This works for:
|
||||
|
||||
- **Values** — we can refer to `Html.text`, `Html.h1`, etc.
|
||||
- **Types** — We can refer to [`Attribute`](http://package.elm-lang.org/packages/elm-lang/html/latest/Html#Attribute) as `Html.Attribute`.
|
||||
- **Types** — We can refer to [`Attribute`](http://package.gren-lang.org/packages/gren-lang/html/latest/Html#Attribute) as `Html.Attribute`.
|
||||
|
||||
So if we add a type annotation to `main` it would look like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
import Html
|
||||
|
||||
main : Html.Html msg
|
||||
@ -34,7 +33,7 @@ main =
|
||||
Html.div [] []
|
||||
```
|
||||
|
||||
We are referring to the [`Html`](http://package.elm-lang.org/packages/elm-lang/html/latest/Html#Html) type, using its *qualified* name `Html.Html`. This can feel weird at first, but it starts feeling natural quite quickly!
|
||||
We are referring to the [`Html`](http://package.gren-lang.org/packages/gren-lang/html/latest/Html#Html) type, using its *qualified* name `Html.Html`. This can feel weird at first, but it starts feeling natural quite quickly!
|
||||
|
||||
> **Note:** Modules do not contain other modules. So the `Html` module *does not* contain the `Html.Attributes` module. Those are separate names that happen to have some overlap. So if you say `import Html` you *do not* get access to `Html.Attributes.style`. You must `import Html.Attributes` module separately.
|
||||
|
||||
@ -45,7 +44,7 @@ We are referring to the [`Html`](http://package.elm-lang.org/packages/elm-lang/h
|
||||
|
||||
It is best practice to always use *qualified* names, but sometimes module names are so long that it becomes unwieldy. This is common for the `Html.Attributes` module. We can use the `as` keyword to help with this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
import Html
|
||||
import Html.Attributes as A
|
||||
|
||||
@ -53,7 +52,7 @@ main =
|
||||
Html.div [ A.style "color" "red" ] [ Html.text "Hello!" ]
|
||||
```
|
||||
|
||||
Saying `import Html.Attributes as A` lets us refer to any value or type in `Html.Attributes` as long as it is qualified with an `A`. So now we can refer to [`style`](http://package.elm-lang.org/packages/elm-lang/html/latest/Html-Attributes#style) as `A.style`.
|
||||
Saying `import Html.Attributes as A` lets us refer to any value or type in `Html.Attributes` as long as it is qualified with an `A`. So now we can refer to [`style`](http://package.gren-lang.org/packages/gren-lang/html/latest/Html-Attributes#style) as `A.style`.
|
||||
|
||||
|
||||
<br>
|
||||
@ -62,7 +61,7 @@ Saying `import Html.Attributes as A` lets us refer to any value or type in `Html
|
||||
|
||||
In quick drafts, maybe you want to use *unqualified* names. You can do that with the `exposing` keyword like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (style)
|
||||
|
||||
@ -84,7 +83,7 @@ Saying `import Html.Attributes exposing (style)` is a bit more reasonable. It me
|
||||
|
||||
There is one last way to import a module. You can combine `as` and `exposing` to try to get a nice balance of qualified names:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
import Html exposing (Html, div, text)
|
||||
import Html.Attributes as A exposing (style)
|
||||
|
||||
@ -100,11 +99,11 @@ Notice that I refer to `A.class` which is qualified and `style` which is unquali
|
||||
|
||||
## Default Imports
|
||||
|
||||
We just learned all the variations of the `import` syntax in Elm. You will use some version of that syntax to `import` any module you ever write.
|
||||
We just learned all the variations of the `import` syntax in Gren. You will use some version of that syntax to `import` any module you ever write.
|
||||
|
||||
It would be the best policy to make it so every module in the whole ecosystem works this way. We thought so in the past at least, but there are some modules that are so commonly used that the Elm compiler automatically adds the imports to every file. These default imports include:
|
||||
It would be the best policy to make it so every module in the whole ecosystem works this way. We thought so in the past at least, but there are some modules that are so commonly used that the Gren compiler automatically adds the imports to every file. These default imports include:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
import Basics exposing (..)
|
||||
import List exposing (List, (::))
|
||||
import Maybe exposing (Maybe(..))
|
||||
@ -121,6 +120,6 @@ import Platform.Sub as Sub exposing (Sub)
|
||||
|
||||
You can think of these imports being at the top of any module you write.
|
||||
|
||||
One could argue that `Maybe` is so fundamental to how we handle errors in Elm code that it is *basically* part of the language. One could also argue that it is extraordinarily annoying to have to import `Maybe` once you get past your first couple weeks with Elm. Either way, we know that default imports are not ideal in some sense, so we have tried to keep the default imports as minimal as possible.
|
||||
One could argue that `Maybe` is so fundamental to how we handle errors in Gren code that it is *basically* part of the language. One could also argue that it is extraordinarily annoying to have to import `Maybe` once you get past your first couple weeks with Gren. Either way, we know that default imports are not ideal in some sense, so we have tried to keep the default imports as minimal as possible.
|
||||
|
||||
> **Note:** Elm performs dead code elimination, so if you do not use something from a module, it is not included in the generated code. So if you `import` a module with hundreds of functions, you do not need to worry about the size of your assets. You will only get what you use!
|
||||
> **Note:** Gren performs dead code elimination, so if you do not use something from a module, it is not included in the generated code. So if you `import` a module with hundreds of functions, you do not need to worry about the size of your assets. You will only get what you use!
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
# Hints for Infinite Types
|
||||
|
||||
Infinite types are probably the trickiest kind of bugs to track down. **Writing down type annotations is usually the fastest way to figure them out.** Let's work through an example to get a feel for how these errors usually work though!
|
||||
@ -8,7 +7,7 @@ Infinite types are probably the trickiest kind of bugs to track down. **Writing
|
||||
|
||||
A common way to get an infinite type error is very small typos. For example, do you see the problem in the following code?
|
||||
|
||||
```elm
|
||||
```gren
|
||||
incrementNumbers list =
|
||||
List.map incrementNumbers list
|
||||
|
||||
@ -18,7 +17,7 @@ incrementNumber n =
|
||||
|
||||
The issue is that `incrementNumbers` calls itself, not the `incrementNumber` function defined below. So there is an extra `s` in this program! Let's focus on that:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
incrementNumbers list =
|
||||
List.map incrementNumbers list -- BUG extra `s` makes this self-recursive
|
||||
```
|
||||
@ -31,7 +30,7 @@ That means that `t1 = List t1`, which is an infinite type! If we start expanding
|
||||
|
||||
The point is mainly that we are in a confusing situation. The types are confusing. This explanation is confusing. The compiler is confused. It is a bad time. But luckily, the more type annotations you add, the better chance there is that you and the compiler can figure things out! So say we change our definition to:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
incrementNumbers : List Int -> List Int
|
||||
incrementNumbers list =
|
||||
List.map incrementNumbers list -- STILL HAS BUG
|
||||
|
@ -1,55 +1,22 @@
|
||||
# Creating an Gren project
|
||||
|
||||
# Creating an Elm project
|
||||
The main goal of `gren init` is to get you to this page!
|
||||
|
||||
The main goal of `elm init` is to get you to this page!
|
||||
|
||||
It just creates an `elm.json` file and a `src/` directory for your code.
|
||||
It just creates an `gren.json` file and a `src/` directory for your code.
|
||||
|
||||
|
||||
## What is `elm.json`?
|
||||
## What is `gren.json`?
|
||||
|
||||
This file describes your project. It lists all of the packages you depend upon, so it will say the particular version of [`elm/core`](https://package.elm-lang.org/packages/elm/core/latest/) and [`elm/html`](https://package.elm-lang.org/packages/elm/html/latest/) that you are using. It makes builds reproducible! You can read a bit more about it [here](https://github.com/elm/compiler/blob/master/docs/elm.json/application.md).
|
||||
This file describes your project. It lists all of the packages you depend upon, so it will say the particular version of [`gren/core`](https://package.gren-lang.org/packages/gren/core/latest/) and [`gren/html`](https://package.gren-lang.org/packages/gren/html/latest/) that you are using. It makes builds reproducible! You can read a bit more about it [here](https://github.com/gren/compiler/blob/master/docs/gren.json/application.md).
|
||||
|
||||
You should generally not edit it by hand. It is better to add new dependencies with commands like `elm install elm/http` or `elm install elm/json`.
|
||||
You should generally not edit it by hand. It is better to add new dependencies with commands like `gren install gren/http` or `gren install gren/json`.
|
||||
|
||||
|
||||
## What goes in `src/`?
|
||||
|
||||
This is where all of your Elm files live. It is best to start with a file called `src/Main.elm`. As you work through [the official guide](https://guide.elm-lang.org/), you can put the code examples in that `src/Main.elm` file.
|
||||
This is where all of your Gren files live. It is best to start with a file called `src/Main.gren`. As you work through [the official guide](https://guide.gren-lang.org/), you can put the code examples in that `src/Main.gren` file.
|
||||
|
||||
|
||||
## How do I compile it?
|
||||
|
||||
Run `elm reactor` in your project. Now you can go to [`http://localhost:8000`](http://localhost:8000) and browse through all the files in your project. If you navigate to `.elm` files, it will compile them for you!
|
||||
|
||||
If you want to do things more manually, you can run `elm make src/Main.elm` and it will produce an `index.html` file that you can look at in your browser.
|
||||
|
||||
|
||||
## How do I structure my directories?
|
||||
|
||||
Many folks get anxious about their project structure. “If I get it wrong, I am doomed!” This anxiety makes sense in languages where refactoring is risky, but Elm is not one of those languages!
|
||||
|
||||
So we recommend that newcomers staying in one file until you get into the 600 to 1000 range. Push out of your comfort zone. Having the experience of being fine in large files will help you understand the boundaries in Elm, rather than just defaulting to the boundaries you learned in another language.
|
||||
|
||||
The talk [The Life of a File](https://youtu.be/XpDsk374LDE) gets into this a lot more. The advice about building modules around a specific [custom type](https://guide.elm-lang.org/types/custom_types.html) is particularly important! You will see that emphasized a lot as you work through the official guide.
|
||||
|
||||
|
||||
## How do I write tests?
|
||||
|
||||
Elm will catch a bunch of errors statically, and I think it is worth skipping tests at first to get a feeling for when tests will actually help you _in Elm_.
|
||||
|
||||
From there, we have a great testing package called [`elm-explorations/test`](https://github.com/elm-explorations/test) that can help you out! It is particularly helpful for teams working on a large codebase. When you are editing code you have never seen before, tests can capture additional details and constraints that are not otherwise apparent!
|
||||
|
||||
|
||||
## How do I start fancier projects?
|
||||
|
||||
I wanted `elm init` to generate as little code as possible. It is mainly meant to get you to this page! If you would like a more elaborate starting point, I recommend starting projects with commands like these:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/evancz/elm-todomvc.git
|
||||
git clone https://github.com/rtfeldman/elm-spa-example.git
|
||||
```
|
||||
|
||||
The idea is that Elm projects should be so simple that nobody needs a tool to generate a bunch of stuff. This also captures the fact that project structure _should_ evolve organically as your application develops, never ending up exactly the same as other projects.
|
||||
|
||||
But if you have something particular you want, I recommend creating your own starter recipe and using `git clone` when you start new projects. That way (1) you can get exactly what you want and (2) we do not end up with a complex `elm init` that ends up being confusing for beginners!
|
||||
You can run `gren make src/Main.gren` and it will produce an `index.html` file that you can look at in your browser.
|
||||
|
@ -1,7 +1,6 @@
|
||||
|
||||
# Hints for Missing Patterns
|
||||
|
||||
Elm checks to make sure that all possible inputs to a function or `case` are handled. This gives us the guarantee that no Elm code is ever going to crash because data had an unexpected shape.
|
||||
Gren checks to make sure that all possible inputs to a function or `case` are handled. This gives us the guarantee that no Gren code is ever going to crash because data had an unexpected shape.
|
||||
|
||||
There are a couple techniques for making this work for you in every scenario.
|
||||
|
||||
@ -10,7 +9,7 @@ There are a couple techniques for making this work for you in every scenario.
|
||||
|
||||
A common scenario is that you want to add a tag to a custom type that is used in a bunch of places. For example, maybe you are working different variations of users in a chat room:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type User
|
||||
= Regular String Int
|
||||
| Anonymous
|
||||
@ -29,7 +28,7 @@ Notice the wildcard pattern in `toName`. This will hurt us! Say we add a `Visito
|
||||
|
||||
So instead, it is better to explicitly list all possible variants, like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type User
|
||||
= Regular String Int
|
||||
| Visitor String
|
||||
@ -52,9 +51,9 @@ Now the compiler will say "hey, what should `toName` do when it sees a `Visitor`
|
||||
|
||||
Imagine that the `User` type appears in 20 or 30 functions across your project. When we add a `Visitor` variant, the compiler points out all the places that need to be updated. That is very convenient, but in a big project, maybe you want to get through it extra quickly.
|
||||
|
||||
In that case, it can be helpful to use [`Debug.todo`](https://package.elm-lang.org/packages/elm-lang/core/latest/Debug#todo) to leave some code incomplete:
|
||||
In that case, it can be helpful to use [`Debug.todo`](https://package.gren-lang.org/packages/gren-lang/core/latest/Debug#todo) to leave some code incomplete:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type User
|
||||
= Regular String Int
|
||||
| Visitor String
|
||||
@ -77,7 +76,7 @@ toName user =
|
||||
|
||||
In this case it is easier to just write the implementation, but the point is that on more complex functions, you can put things off a bit.
|
||||
|
||||
The Elm compiler is actually aware of `Debug.todo` so when it sees it in a `case` like this, it will crash with a bunch of helpful information. It will tell you:
|
||||
The Gren compiler is actually aware of `Debug.todo` so when it sees it in a `case` like this, it will crash with a bunch of helpful information. It will tell you:
|
||||
|
||||
1. The name of the module that contains the code.
|
||||
2. The line numbers of the `case` containing the TODO.
|
||||
@ -90,9 +89,9 @@ I tend to use `Debug.todo` as the message when my goal is to go quick because it
|
||||
|
||||
## A list that definitely is not empty
|
||||
|
||||
This can come up from time to time, but Elm **will not** let you write code like this:
|
||||
This can come up from time to time, but Gren **will not** let you write code like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
last : List a -> a
|
||||
last list =
|
||||
case list of
|
||||
@ -105,7 +104,7 @@ last list =
|
||||
|
||||
This is no good. It does not handle the empty list. There are two ways to handle this. One is to make the function return a `Maybe` like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
last : List a -> Maybe a
|
||||
last list =
|
||||
case list of
|
||||
@ -123,7 +122,7 @@ This is nice because it lets users know that there might be a failure, so they c
|
||||
|
||||
The other option is to “unroll the list” one level to ensure that no one can ever provide an empty list in the first place:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
last : a -> List a -> a
|
||||
last first rest =
|
||||
case rest of
|
||||
|
@ -1,12 +1,11 @@
|
||||
|
||||
# How to optimize Elm code
|
||||
# How to optimize Gren code
|
||||
|
||||
When you are serving a website, there are two kinds of optimizations you want to do:
|
||||
|
||||
1. **Asset Size** — How can we send as few bits as possible?
|
||||
2. **Performance** — How can those bits run as quickly as possible?
|
||||
|
||||
It turns out that Elm does really well on both! We have [very small assets](https://elm-lang.org/news/small-assets-without-the-headache) and [very fast code](https://elm-lang.org/news/blazing-fast-html-round-two) when compared to the popular alternatives.
|
||||
It turns out that Gren does really well on both! We have [very small assets](https://gren-lang.org/news/small-assets-without-the-headache) and [very fast code](https://gren-lang.org/news/blazing-fast-html-round-two) when compared to the popular alternatives.
|
||||
|
||||
Okay, but how do we get those numbers?
|
||||
|
||||
@ -15,36 +14,36 @@ Okay, but how do we get those numbers?
|
||||
|
||||
Step one is to compile with the `--optimize` flag. This does things like shortening record field names and unboxing values.
|
||||
|
||||
Step two is to call `uglifyjs` with a bunch of special flags. The flags unlock optimizations that are unreliable in normal JS code, but because Elm does not have side-effects, they work fine for us!
|
||||
Step two is to call `uglifyjs` with a bunch of special flags. The flags unlock optimizations that are unreliable in normal JS code, but because Gren does not have side-effects, they work fine for us!
|
||||
|
||||
Putting those together, here is how I would optimize `src/Main.elm` with two terminal commands:
|
||||
Putting those together, here is how I would optimize `src/Main.gren` with two terminal commands:
|
||||
|
||||
```bash
|
||||
elm make src/Main.elm --optimize --output=elm.js
|
||||
uglifyjs elm.js --compress "pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe" | uglifyjs --mangle --output elm.min.js
|
||||
gren make src/Main.gren --optimize --output=gren.js
|
||||
uglifyjs gren.js --compress "pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe" | uglifyjs --mangle --output gren.min.js
|
||||
```
|
||||
|
||||
After this you will have an `elm.js` and a significantly smaller `elm.min.js` file!
|
||||
After this you will have an `gren.js` and a significantly smaller `gren.min.js` file!
|
||||
|
||||
**Note 1:** `uglifyjs` is called twice there. First to `--compress` and second to `--mangle`. This is necessary! Otherwise `uglifyjs` will ignore our `pure_funcs` flag.
|
||||
|
||||
**Note 2:** If the `uglifyjs` command is not available in your terminal, you can run the command `npm install uglify-js --global` to download it. You probably already have `npm` from getting `elm repl` working, but if not, it is bundled with [nodejs](https://nodejs.org/).
|
||||
**Note 2:** If the `uglifyjs` command is not available in your terminal, you can run the command `npm install uglify-js --global` to download it. You probably already have `npm` from getting `gren repl` working, but if not, it is bundled with [nodejs](https://nodejs.org/).
|
||||
|
||||
## Scripts
|
||||
|
||||
It is hard to remember all that, so it is probably a good idea to write a script that does it.
|
||||
|
||||
I would maybe want to run `./optimize.sh src/Main.elm` and get out `elm.js` and `elm.min.js`, so on Mac or Linux, I would make a script called `optimize.sh` like this:
|
||||
I would maybe want to run `./optimize.sh src/Main.gren` and get out `gren.js` and `gren.min.js`, so on Mac or Linux, I would make a script called `optimize.sh` like this:
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
js="elm.js"
|
||||
min="elm.min.js"
|
||||
js="gren.js"
|
||||
min="gren.min.js"
|
||||
|
||||
elm make --optimize --output=$js $@
|
||||
gren make --optimize --output=$js $@
|
||||
|
||||
uglifyjs $js --compress "pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe" | uglifyjs --mangle --output $min
|
||||
|
||||
@ -55,5 +54,5 @@ echo "Gzipped size: $(cat $min | gzip -c | wc -c) bytes"
|
||||
|
||||
It also prints out all the asset sizes for you! Your server should be configured to gzip the assets it sends, so the last line is telling you how many bytes would _actually_ get sent to the user.
|
||||
|
||||
Again, the important commands are `elm` and `uglifyjs` which work on any platform, so it should not be too tough to do something similar on Windows.
|
||||
Again, the important commands are `gren` and `uglifyjs` which work on any platform, so it should not be too tough to do something similar on Windows.
|
||||
|
||||
|
@ -1,22 +1,21 @@
|
||||
|
||||
# No Ports in Packages
|
||||
|
||||
The package ecosystem is one of the most important parts of Elm. Right now, our ecosystem has some compelling benefits:
|
||||
The package ecosystem is one of the most important parts of Gren. Right now, our ecosystem has some compelling benefits:
|
||||
|
||||
- There are many obvious default packages that work well.
|
||||
- Adding dependencies cannot introduce runtime exceptions.
|
||||
- Patch changes cannot lead to surprise build failures.
|
||||
|
||||
These are really important factors if you want to *quickly* create *reliable* applications. The Elm community thinks this is valuable.
|
||||
These are really important factors if you want to *quickly* create *reliable* applications. The Grencommunity thinks this is valuable.
|
||||
|
||||
Other communities think that the *number* of packages is a better measure of ecosystem health. That is a fine metric to use, but it is not the one we use for Elm. We would rather have 50 great packages than 100k packages of wildly varying quality.
|
||||
Other communities think that the *number* of packages is a better measure of ecosystem health. That is a fine metric to use, but it is not the one we use for Gren. We would rather have 50 great packages than 100k packages of wildly varying quality.
|
||||
|
||||
|
||||
## So what about ports?
|
||||
|
||||
Imagine you install a new package that claims to support `localStorage`. You get it set up, working through any compile errors. You run it, but it does not seem to work! After trying to figure it out for hours, you realize there is some poorly documented `port` to hook up...
|
||||
|
||||
Okay, now you need to hook up some JavaScript code. Is that JS file in the Elm package? Or is it on `npm`? Wait, what version on `npm` though? And is this patch version going to work as well? Also, how does this file fit into my build process? And assuming we get through all that, maybe the `port` has the same name as one of the ports in your project. Or it clashes with a `port` name in another package.
|
||||
Okay, now you need to hook up some JavaScript code. Is that JS file in the Gren package? Or is it on `npm`? Wait, what version on `npm` though? And is this patch version going to work as well? Also, how does this file fit into my build process? And assuming we get through all that, maybe the `port` has the same name as one of the ports in your project. Or it clashes with a `port` name in another package.
|
||||
|
||||
**Suddenly adding dependencies is much more complicated and risky!** An experienced developer would always check for ports up front, spending a bunch of time manually classifying unacceptable packages. Most people would not know to do that and learn all the pitfalls through personal experience, ultimately spending even *more* time than the person who defensively checks to avoid these issues.
|
||||
|
||||
@ -25,8 +24,8 @@ So “ports in packages” would impose an enormous cost on application develope
|
||||
|
||||
## Conclusion
|
||||
|
||||
Our wager with the Elm package ecosystem is that it is better to get a package *right* than to get it *right now*. So while we could use “ports in packages” as a way to get twenty `localStorage` packages of varying quality *right now*, we are choosing not to go that route. Instead we ask that developers use ports directly in their application code, getting the same result a different way.
|
||||
Our wager with the Gren package ecosystem is that it is better to get a package *right* than to get it *right now*. So while we could use “ports in packages” as a way to get twenty `localStorage` packages of varying quality *right now*, we are choosing not to go that route. Instead we ask that developers use ports directly in their application code, getting the same result a different way.
|
||||
|
||||
Now this may not be the right choice for your particular project, and that is okay! We will be expanding our core libraries over time, as explained [here](https://github.com/elm-lang/projects/blob/master/roadmap.md#where-is-the-localstorage-package), and we hope you will circle back later to see if Elm has grown into a better fit!
|
||||
Now this may not be the right choice for your particular project, and that is okay! We will be expanding our core libraries over time, as explained [here](https://github.com/gren-lang/projects/blob/master/roadmap.md#where-is-the-localstorage-package), and we hope you will circle back later to see if Gren has grown into a better fit!
|
||||
|
||||
If you have more questions about this choice or what it means for your application, please come ask in [the Elm slack](http://elmlang.herokuapp.com/). Folks are friendly and happy to help out! Chances are that a `port` in your application will work great for your case once you learn more about how they are meant to be used.
|
||||
If you have more questions about this choice or what it means for your application, please come ask in [the Gren slack](http://grenlang.herokuapp.com/). Folks are friendly and happy to help out! Chances are that a `port` in your application will work great for your case once you learn more about how they are meant to be used.
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
# Hints for Recursive Type Aliases
|
||||
|
||||
At the root of this issue is the distinction between a `type` and a `type alias`.
|
||||
@ -8,7 +7,7 @@ At the root of this issue is the distinction between a `type` and a `type alias`
|
||||
|
||||
When you create a type alias, you are just creating a shorthand to refer to an existing type. So when you say the following:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type alias Time = Float
|
||||
|
||||
type alias Degree = Float
|
||||
@ -18,7 +17,7 @@ type alias Weight = Float
|
||||
|
||||
You have not created any *new* types, you just made some alternate names for `Float`. You can write down things like this and it'll work fine:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
add : Time -> Degree -> Weight
|
||||
add time degree =
|
||||
time + degree
|
||||
@ -26,7 +25,7 @@ add time degree =
|
||||
|
||||
This is kind of a weird way to use type aliases though. The typical usage would be for records, where you do not want to write out the whole thing every time. Stuff like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type alias Person =
|
||||
{ name : String
|
||||
, age : Int
|
||||
@ -39,9 +38,9 @@ It is much easier to write down `Person` in a type, and then it will just expand
|
||||
|
||||
## Recursive type aliases?
|
||||
|
||||
Okay, so let's say you have some type that may contain itself. In Elm, a common example of this is a comment that might have subcomments:
|
||||
Okay, so let's say you have some type that may contain itself. In Gren, a common example of this is a comment that might have subcomments:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type alias Comment =
|
||||
{ message : String
|
||||
, upvotes : Int
|
||||
@ -52,7 +51,7 @@ type alias Comment =
|
||||
|
||||
Now remember that type *aliases* are just alternate names for the real type. So to make `Comment` into a concrete type, the compiler would start expanding it out.
|
||||
|
||||
```elm
|
||||
```gren
|
||||
{ message : String
|
||||
, upvotes : Int
|
||||
, downvotes : Int
|
||||
@ -79,7 +78,7 @@ The compiler cannot deal with values like this. It would just keep expanding for
|
||||
|
||||
In cases where you want a recursive type, you need to actually create a brand new type. This is what the `type` keyword is for. A simple example of this can be seen when defining a linked list:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type List
|
||||
= Empty
|
||||
| Node Int List
|
||||
@ -92,7 +91,7 @@ So let's return to wanting to represent a `Comment` that may have responses. The
|
||||
|
||||
### Obvious, but kind of annoying
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type Comment =
|
||||
Comment
|
||||
{ message : String
|
||||
@ -104,7 +103,7 @@ type Comment =
|
||||
|
||||
Now let's say you want to register an upvote on a comment:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
upvote : Comment -> Comment
|
||||
upvote (Comment comment) =
|
||||
Comment { comment | upvotes = 1 + comment.upvotes }
|
||||
@ -115,7 +114,7 @@ It is kind of annoying that we now have to unwrap and wrap the record to do anyt
|
||||
|
||||
### Less obvious, but nicer
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type alias Comment =
|
||||
{ message : String
|
||||
, upvotes : Int
|
||||
@ -128,7 +127,7 @@ type Responses = Responses (List Comment)
|
||||
|
||||
In this world, we introduce the `Responses` type to capture the recursion, but `Comment` is still an alias for a record. This means the `upvote` function looks nice again:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
upvote : Comment -> Comment
|
||||
upvote comment =
|
||||
{ comment | upvotes = 1 + comment.upvotes }
|
||||
@ -141,7 +140,7 @@ So rather than having to unwrap a `Comment` to do *anything* to it, you only hav
|
||||
|
||||
It is also possible to build type aliases that are *mutually* recursive. That might be something like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type alias Comment =
|
||||
{ message : String
|
||||
, upvotes : Int
|
||||
|
@ -1,14 +1,13 @@
|
||||
|
||||
# REPL
|
||||
|
||||
The REPL lets you interact with Elm values and functions in your terminal.
|
||||
The REPL lets you interact with Gren values and functions in your terminal.
|
||||
|
||||
|
||||
## Use
|
||||
|
||||
You can type in expressions, definitions, custom types, and module imports using normal Elm syntax.
|
||||
You can type in expressions, definitions, custom types, and module imports using normal Gren syntax.
|
||||
|
||||
```elm
|
||||
```gren
|
||||
> 1 + 1
|
||||
2 : number
|
||||
|
||||
@ -18,7 +17,7 @@ You can type in expressions, definitions, custom types, and module imports using
|
||||
|
||||
The same can be done with definitions and custom types:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
> fortyTwo = 42
|
||||
42 : number
|
||||
|
||||
@ -51,9 +50,9 @@ The same can be done with definitions and custom types:
|
||||
"Hey again!" : String
|
||||
```
|
||||
|
||||
When you run `elm repl` in a project with an [`elm.json`](https://github.com/elm/compiler/blob/master/docs/elm.json/application.md) file, you can import any module available in the project. So if your project has an `elm/html` dependency, you could say:
|
||||
When you run `gren repl` in a project with an [`gren.json`](https://github.com/gren/compiler/blob/master/docs/gren.json/application.md) file, you can import any module available in the project. So if your project has an `gren/html` dependency, you could say:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
> import Html exposing (Html)
|
||||
|
||||
> Html.text "hello"
|
||||
|
@ -1,9 +1,8 @@
|
||||
|
||||
# Variable Shadowing
|
||||
|
||||
Variable shadowing is when you define the same variable name twice in an ambiguous way. Here is a pretty reasonable use of shadowing:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
viewName : Maybe String -> Html msg
|
||||
viewName name =
|
||||
case name of
|
||||
@ -16,7 +15,7 @@ viewName name =
|
||||
|
||||
I define a `name` with type `Maybe String` and then in that second branch, I define a `name` that is a `String`. Now that there are two `name` values, it is not 100% obvious which one you want in that second branch.
|
||||
|
||||
Most linters produce warnings on variable shadowing, so Elm makes “best practices” the default. Just rename the first one to `maybeName` and move on.
|
||||
Most linters produce warnings on variable shadowing, so Gren makes “best practices” the default. Just rename the first one to `maybeName` and move on.
|
||||
|
||||
This choice is relatively uncommon in programming languages though, so I want to provide the reasoning behind it.
|
||||
|
||||
@ -27,7 +26,7 @@ The code snippet from above is the best case scenario for variable shadowing. It
|
||||
|
||||
In a large module that is evolving over time, this is going to cause bugs in a very predictable way. You will have two definitions, separated by hundreds of lines. For example:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
name : String
|
||||
name =
|
||||
"Tom"
|
||||
@ -41,7 +40,7 @@ viewName name =
|
||||
|
||||
Okay, so the `viewName` function has an argument `name` and it uses it three times. Maybe the `viewName` function is 50 lines long in total, so those uses are not totally easy to see. This is fine so far, but say your colleague comes along five months later and wants to support first and last names. They refactor the code like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
viewName : String -> String -> Html msg
|
||||
viewName firstName lastName =
|
||||
... name ... name ... name ...
|
||||
@ -67,4 +66,4 @@ If you are still skeptical, I encourage you can play around with the number of e
|
||||
|
||||
## Summary
|
||||
|
||||
Without shadowing, the code easier to read and folks spend less time on pointless debugging. The net outcome is that folks have more time to make something wonderful with Elm!
|
||||
Without shadowing, the code easier to read and folks spend less time on pointless debugging. The net outcome is that folks have more time to make something wonderful with Gren!
|
||||
|
@ -1,11 +1,10 @@
|
||||
|
||||
# From Tuples to Records
|
||||
|
||||
The largest tuple possible in Elm has three entries. Once you get to four, it is best to make a record with named entries.
|
||||
The largest tuple possible in Gren has three entries. Once you get to four, it is best to make a record with named entries.
|
||||
|
||||
For example, it is _conceivable_ to represent a rectangle as four numbers like `(10,10,100,100)` but it would be more self-documenting to use a record like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
type alias Rectangle =
|
||||
{ x : Float
|
||||
, y : Float
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
# Hints for Type Annotation Problems
|
||||
|
||||
At the root of this kind of issue is always the fact that a type annotation in your code does not match the corresponding definition. Now that may mean that the type annotation is "wrong" or it may mean that the definition is "wrong". The compiler cannot figure out your intent, only that there is some mismatch.
|
||||
@ -10,7 +9,7 @@ This document is going to outline the various things that can go wrong and show
|
||||
|
||||
The most common issue is with user-defined type variables that are too general. So let's say you have defined a function like this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
addPair : (a, a) -> a
|
||||
addPair (x, y) =
|
||||
x + y
|
||||
@ -18,7 +17,7 @@ addPair (x, y) =
|
||||
|
||||
The issue is that the type annotation is saying "I will accept a tuple containing literally *anything*" but the definition is using `(+)` which requires things to be numbers. So the compiler is going to infer that the true type of the definition is this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
addPair : (number, number) -> number
|
||||
```
|
||||
|
||||
@ -31,7 +30,7 @@ In cases like this, you want to go with whatever the compiler inferred. It is go
|
||||
|
||||
It is also possible to have a type annotation that clashes with itself. This is probably more rare, but someone will run into it eventually. Let's use another version of `addPair` with problems:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
addPair : (Int, Int) -> number
|
||||
addPair (x, y) =
|
||||
x + y
|
||||
@ -44,7 +43,7 @@ In this case the annotation says we should get a `number` out, but because we we
|
||||
|
||||
A quite tricky case is when an outer type annotation clashes with an inner type annotation. Here is an example of this:
|
||||
|
||||
```elm
|
||||
```gren
|
||||
filter : (a -> Bool) -> List a -> List a
|
||||
filter isOkay list =
|
||||
let
|
||||
|
Loading…
Reference in New Issue
Block a user