Setting values in aliased records - elm

I'm just starting out with Elm, so be nice:)
In the "Random" example of the official Elm Guide the model seems to be initialized with the value 1 like this:
type alias Model =
{ dieFace : Int
}
init : (Model, Cmd Msg)
init =
(Model 1, Cmd.none)
My understanding of this is that the code
Model 1
sets the value of the dieFace attribute in the record. Is this correct and if so: what is this strange syntax for setting the attribute of a record? I would have expected something along the lines of
{ model | dieFace = 1 }

Model is a type alias for a record which has a single int value called dieFace.
There are a few ways to create a value of that type:
Model 1 -- treats Model as a function with a single parameter
{ dieFace = 1 } -- creates a record that happens to coincide with the Model alias
The strange syntax you see in { model | dieFace = 1 } is short-hand for creating a new value based off an existing record value but changing one or more fields. It perhaps doesn't make much sense when your record type has a single field so let's create an arbitrary example:
type alias ColoredDie = { dieFace: Int, color: String }
You can play around in the Elm REPL and perhaps this will help it make sense:
> blue3 = ColoredDie 3 "blue"
{ dieFace = 3, color = "blue" } : Repl.ColoredDie
> red3 = { blue3 | color = "red" }
{ dieFace = 3, color = "red" } : { dieFace : Int, color : String }
> red4 = { red3 | dieFace = 4 }
{ dieFace = 4, color = "red" } : { color : String, dieFace : number }
> green6 = { red4 | color = "green", dieFace = 6 }
{ dieFace = 6, color = "green" } : { color : String, dieFace : number }
You can read up on Elm's record syntax here.

Related

Map within a map in terraform variables

Does anyone know if it's possible with possibly code snipits representing whether I can create a map variable within a map variable in terraform variables?
variable "var" {
type = map
default = {
firstchoice = {
firstAChoice ="foo"
firstBChoice = "bar"
}
secondchoice = {
secondAChoice = "foobar"
secondBChoice = "barfoo"
}
}
}
If anyone has any insight to whether this is possible or any documentation that elaborates that would be great.
Yes, it's possible to have map variable as value of map variable key. Your variable just needed right indentation. Also I am putting ways to access that variable.
variable "var" {
default = {
firstchoice = {
firstAChoice = "foo"
firstBChoice = "bar"
}
secondchoice = {
secondAChoice = "foobar"
secondBChoice = "barfoo"
}
}
}
To access entire map value of a map key firstchoice, you can try following
value = "${var.var["firstchoice"]}"
output:
{
firstAChoice = foo
firstBChoice = bar
}
To access specific key of that map key (example firstAChoice), you can try
value = "${lookup(var.var["firstchoice"],"firstAChoice")}"
output: foo
Would this syntax be possible? ${var.var[firstchoice[firstAchoice]]}
With Terraform 0.12+ nested blocks are supported seamlessly. Extending #Avichal Badaya's answer to explain it using an example:
# Nested Variable
variable "test" {
default = {
firstchoice = {
firstAChoice = "foo"
firstBChoice = "bar"
}
secondchoice = {
secondAChoice = "foobar"
secondBChoice = "barfoo"
}
thirdchoice = {
thirdAChoice = {
thirdBChoice = {
thirdKey = "thirdValue"
}
}
}
}
}
# Outputs
output "firstchoice" {
value = var.test["firstchoice"]
}
output "FirstAChoice" {
value = var.test["firstchoice"]["firstAChoice"]
}
output "thirdKey" {
value = var.test["thirdchoice"]["thirdAChoice"]["thirdBChoice"]["thirdKey"]
}
Applying the above, you can verify that Terraform map nesting is now quite powerful and this makes a lot of things easier.
# Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
# Outputs:
firstchoice = {
"firstAChoice" = "foo"
"firstBChoice" = "bar"
}
thirdKey = thirdValue
For more complex structures and Rich Value Types, see HashiCorp Terraform 0.12 Preview: Rich Value Types

Transform string in key variable name

There is the possibility of converting a string to a key, eg:
type alias Model =
{ sun : Int -- Init with 0
, moon : Int -- Init with 0
}
What I want to achieve:
let
userSelect = "sun";
in
({ model | userSelect = 1 }, Cmd.none) -- ugly to be easy to understand
After model.sun should 1
You won't be able to do exactly what you want as access to records does not work that way. In your case i would recommend a Dictionary
type alias Model =
{ planets : Dict String Int
}
planets =
Dict.fromList
[ ("sun", 0)
, ("moon": 0)
]
model = { planets = planets }
and then
let
userSelect = "sun";
in
({ model | planets = Dict.insert userSelect 1 model.planets }, Cmd.none)

Remove item of record without knowing all item

I have this simulation:
init : (Model, Cmd Msg)
init = ({ dog = List Dog }, Cmd.none)
type alias Dog =
{ name : String
, age : Int
, price : Float
, extra = List Extra
}
type alias Extra =
{ allergies : List String
, wishes : List String
}
[{ name = "Hot"
, age = 1
, price = 300.5
, extra = [{...}]
},
{ name = "Dog"
, age = 3
, price = 150.0
, extra = [{...}]
}]
And I want to remove only 'extras' of Dog, in determined part of the code:
[{ name = "Hot"
, age = 1
, price = 300.5
},
{ name = "Dog"
, age = 3
, price = 150.0
}]
I can do this by mapping the entire list and generating a new one by removing 'extra' occurrence:
removeExtraOfDogs dogList =
(dogList |> List.map (\dog ->
{ name = dog.name
, age = dog.age
, price = dog.price
}
))
but I want to make it dynamic to just pass the extra to remove, without having to know what variables there are in the type and recreate it
Elm used to have this feature but it was removed a while ago. But, based on your use case described in a comment, I don't think you need this feature. You can instead use extensible record feature of Elm to allow passing different records into a function as long as they contain a fixed set of fields.
For example, let's say you have two types with name and age fields and having an extra incompatible field:
type alias Foo = { name : String, age : Int, extra : List String }
type alias Bar = { name : String, age : Int, extra : Int }
You can define a function that takes a record with a name field of type String and age of type Int and any extra fields:
encode : { r | name : String, age : Int } -> String
encode record = record.name ++ "," ++ toString record.age
You'll can now pass both Foo and Bar to this function because they both satisfy the requirements of the type signature.

How to upate a record that has another record within it?

I have a record :
type alias Point = {x : Int, y : Int}
I have another record like this :
type alias SuperPoint = {p : Point, z : Int}
p = Point 5 10
sp = SuperPoint p 15
Now if I need to update SuperPoint.z I can do this :
{sp | z = 20}
How do I update SuperPoint.Point?
sp2 =
let
p2 = { p | x = 42 }
in
{ sp | p = p2 }
Right now you have four ways to update:
Two of them you can find below in code
third is using Monocle.
fourth: Re-structure your model with Dictionaries and handle the update in a proper generic way
A the end there is also a code which doesn't work, but could be useful, if that worked that way
Example in elm-0.18
import Html exposing (..)
model =
{ left = { x = 1 }
}
updatedModel =
let
left =
model.left
newLeft =
{ left | x = 10 }
in
{ model | left = newLeft }
updateLeftX x ({ left } as model) =
{ model | left = { left | x = x } }
updatedModel2 =
updateLeftX 11 model
main =
div []
[ div [] [ text <| toString model ]
, div [] [ text <| toString updatedModel ]
, div [] [ text <| toString updatedModel2 ]
]
Examples from https://groups.google.com/forum/#!topic/elm-discuss/CH77QbLmSTk
-- nested record updates are not allowed
-- https://github.com/elm-lang/error-message-catalog/issues/159
updatedModel3 =
{ model | left = { model.left | x = 12 } }
-- The .field shorthand does not for the update syntax
-- https://lexi-lambda.github.io/blog/2015/11/06/functionally-updating-record-types-in-elm/
updateInt : Int -> (data -> Int) -> data -> data
updateInt val accessor data =
{ data | accessor = val }
updatedModel4 =
{ model | left = updateInt 13 .x model.left }

Errors that I don't know how to fix

#pragma strict
var targetscript : Diamond;
var yellow : Color(1,0.92,0.016,1);
var cyan : Color(0,1,1,1);
var green : Color(0,1,0,1);
var red : Color(1,0,0,1);
var magenta : Color(1,0,1,1);
var black : Color(0,0,0,1);
function Start () {
gameObject.camera.backgroundColor = yellow;
}
function Update () {
if (targetscript.score > 4) {
gameObject.camera.backgroundColor = Color.Lerp(yellow, cyan);
}
if (targetscript.score > 9) {
gameObject.camera.backgroundColor = Color.Lerp(cyan, green);
}
if (targetscript.score > 14) {
gameObject.camera.backgroundColor = Color.Lerp(green, red);
}
if (targetscript.score > 19) {
gameObject.camera.backgroundColor = Color.Lerp(red, magenta);
}
if (targetscript.score > 24) {
gameObject.camera.backgroundColor = Color.Lerp(magenta);
}
}
It gives me these errors:
Assets/Scripts/colour.js(4,22): UCE0001: ';' expected. Insert a semicolon at the end.
Assets/Scripts/colour.js(4,22): BCE0044: expecting EOF, found '0.92'.
Assets/Scripts/colour.js(4,21): BCE0044: expecting ), found ','.
Assets/Scripts/colour.js(4,19): UCE0001: ';' expected. Insert a semicolon at the end.
I don't find any missing semicolons! I don't find anything wrong with the colors! why does it give me so many errors? I checked the script more than 5 times but I don't find anything wrong! Does anyone have any ideas? Thanks in advance
Since you are doing variable declaration with assignment to a class I think each of the color declaration need to look similar to this:
var yellow = new Color(1,0.92,0.016,1);
This creates a yellow variable with a Color type and we create a new instance of it and assign it to yellow.
var xxx: yyy = zzz;
This is how you declare a variable in Unityscript, where xxx is the name of the variable, yyy is the type of the variable and zzz is the value.
In your case, you wanted to define a type but you defined a value instead, thus the error appeared.
Change all of them to be:
var yellow : Color;
var cyan : Color;
var green : Color;
var red : Color;
var magenta : Color;
var black : Color;
Then in Start() function, give them value:
yellow = Color(1,0.92,0.016,1);
cyan = Color(0,1,1,1);
green = Color(0,1,0,1);
red = Color(1,0,0,1);
magenta = Color(1,0,1,1);
black = Color(0,0,0,1);
you could do it like this
#pragma strict
var targetscript : Diamond;
var yellow : Color = Color(1,0.92,0.016,1);
var cyan : Color = Color(0,1,1,1);
var green : Color = Color(0,1,0,1);
var red : Color = Color(1,0,0,1);
var magenta : Color = Color(1,0,1,1);
var black : Color = Color(0,0,0,1);
function Start () {
gameObject.camera.backgroundColor = yellow;
}
function Update () {
if (targetscript.score > 4) {
gameObject.camera.backgroundColor = Color.Lerp(yellow, cyan);
}
if (targetscript.score > 9) {
gameObject.camera.backgroundColor = Color.Lerp(cyan, green);
}
if (targetscript.score > 14) {
gameObject.camera.backgroundColor = Color.Lerp(green, red);
}
if (targetscript.score > 19) {
gameObject.camera.backgroundColor = Color.Lerp(red, magenta);
}
if (targetscript.score > 24) {
gameObject.camera.backgroundColor = Color.Lerp(magenta);
}
}