Get constructor by value option in Elm (0.19.1) - elm

I have some types:
type alias Type1 = { name : String }
type alias Type2 = { name : String, age : Int }
type S
= S1 Type1
| S2 Type2
So, when I want to build my info I can do the following:
a = S1 { name = "Name1" }
b = S2 { name = "Name2", age = 100 }
Now, I would like to get its constructor (S1 or S2) based on the info passed as parameter to the constructorByData function. Examples:
constructorByData a -- It should be returned `S1`
constructorByData b -- It should be returned `S2`
This is my aproach:
constructorByData : S -> (a -> S)
constructorByData s =
case s of
S1 _ -> S1
S2 _ -> S2
Please, note that the above code does not work. I get the following error:
-- TYPE MISMATCH ---------------------------------------------------------- REPL
Something is off with the 2nd branch of this `case` expression:
14| S2 _ -> S2
^^
This `S2` value is a:
Type2 -> S
But the type annotation on `constructorByData` says it should be:
a -> S
-- TYPE MISMATCH ---------------------------------------------------------- REPL
Something is off with the 1st branch of this `case` expression:
13| S1 _ -> S1
^^
This `S1` value is a:
Type1 -> S
But the type annotation on `constructorByData` says it should be:
a -> S
The main reason I want to do this is because I want to do the following by generalizing the message:
type Field
= Name
| Age
type Msg
= UpdateFieldString S Field String
| UpdateFieldInt S Field Int
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
UpdateFieldString s field value -> updateFieldString model s field value
UpdateFieldInt s field value -> updateFieldInt model s field value
And is for this I need the constructor in updateField{String, Int} functions

Related

Update Elm record with multiple constructors

Is there a way to modify field for a type with multiple constructors?
type U = S | C { xxx : String }
This is not a record, so it has no fields to update!
5| let c = C { xxx = "DD"} in case c of C {} -> { c | xxx = "ZZ" }
^
This `c` value is a:
U
But I need a record!
In type U = S | C { xxx : String }, xxx is not a field of U. It is a field of the record contained in C. Those are separate types that can be deconstructed into separate values. And the way to do that is to use case to match the C constructor and bind the contained value to a name (or further deconstruct it) so we can refer to it on the right side of the ->.
But then you also need to handle the possibility of c being S. What should be returned then? Maybe a default? Or maybe you actually want to return a U? I've assumed the former here, but the latter would just be construction values of U as you would anywhere else.
let
c =
C { xxx = "DD" }
in
case c of
C record ->
{ record | xxx = "ZZ" }
S ->
-- What to do here?
{ xxx = "default?" }

Elm: Access value of custom type

I have a custom type in Elm to handle error branching. Basically, I have an input, which gives Strings and I need to convert it to Ints.
type Seconds
= Error Int
| Valid Int
type alias Model =
{ timeBetweenExercises : Seconds
, roundsSequenceOne : Seconds
, roundsSequenceTwo : Seconds
, roundsSequenceThree : Seconds
, repititionsPerExercise : Seconds
, secondsPerExercise : Seconds
, totalTrainingDuration : Seconds
}
init : Model
init =
{ timeBetweenExercises = Valid 3
, roundsSequenceOne = Valid 3
, roundsSequenceTwo = Valid 3
, roundsSequenceThree = Valid 3
, repetitionsPerExercise = Valid 6
, secondsPerExercise = Valid 6
, totalTrainingDuration = Valid 6
}
I got the idea for the custom type from Evan's "Life of a file" talk. I want to remember the number when there is an error (e.g. a user entered a string instead of a number). Here is the attempt at my update function:
update : Msg -> Model -> Model
update msg model =
case msg of
TimeBetweenExercisesChanged newTime ->
case String.toInt newTime of
Nothing ->
{ model | timeBetweenExercises = Error model.timeBetweenExercises }
Just time ->
{ model | timeBetweenExercises = Valid time }
My problem is, that the compiler yells at me because because model.timeBetweenExercises is of type Seconds. Is there a way I can only get the Int value of the custom type?
It seems to me that the model is wrong, for two reason:
If you have a value that is common across all cases, it's usually more convenient to move it out and up a level.
Your update function suggests that the Error state doesn't actually describe the value it holds, as you're just throwing away newTime if invalid and using the old time for the Error case instead.
Based on that, I'd suggest the following model instead:
type alias TimeInput =
{ seconds: Int
, state: TimeInputState
}
type TimeInputState = Error | Valid
type alias Model =
{ timeBetweenExercises : TimeInput
...
}
and to change your update function to this:
update : Msg -> Model -> Model
update msg model =
case msg of
TimeBetweenExercisesChanged newTime ->
case String.toInt newTime of
Nothing ->
{ model
| timeBetweenExercises =
{ seconds = model.timeBetweenExercises.seconds
, state = Error
}
}
Just time ->
{ model
| timeBetweenExercises =
{ seconds = time
, state = Valid
}
}
Otherwise, you could always just make a function to extract the Int no matter the case:
getSeconds : Seconds -> Int
getSeconds time =
case time of
Error seconds -> seconds
Valid seconds -> seconds

What is the most idiomatic way of representing errors in F#

I'm working on F# project and I wonder what is the best practice to return domain error using Result type in F#. There are several ways of doing it which I consider:
Inherited exceptions
type DomainException(message) =
inherit Exception(message)
type ItemNotFoundException(item) =
inherit DomainException(sprintf "Item %s is not found" item)
let findItem item =
match item with
| Some x -> Ok x
| None -> Error(new ItemNotFoundException("someitem"))
Custom record type
type DomainError =
{ Name : string
Message : string }
let findItem item =
match item with
| Some x -> Ok x
| None ->
Error({ Name = "ItemNotFound"
Message = "Item someitem is not found" })
Discriminated union of record type
type DomainErrorTypes =
| ItemNotFoundError of DomainError
| ItemInvalidFormat of DomainError
let findItem item =
match item with
| Some x -> Ok x
| None ->
{ Name = "ItemNotFound"
Message = "Item someitem is not found" }
|> ItemNotFoundError
|> Error
So which way is more idiomatic and convenient to use? I also will be happy to see better options.
Typically it would be a discriminated union. Every error requires different details to accompany the message. For instance:
type DomainErrorTypes =
| ItemNotFound of ItemId
| FileNotFound of string
| InvalidFormat of format
| IncompatibleItems of Item * Item
| SQLError of code:int * message:string
| ...
You can also capture some exceptions (not necessarily all):
| ...
| Exception of exn

Easiest way of defining and using of Global Variable

"first part" &&&& fun _ ->
let ident
"second part" &&&& fun _ ->
ident ....
I need to use variable "ident".
I just need to pass value of variable from first part of test to second one...
I want to ask you if there is any easy way how to define and use global variable or even if you have better (and easy) idea of doing that
Keep in mind, please, that I am a beginner, so I would prefer easier ones.
Global variables will often make your code difficult to work with - particularly if they are mutable.
Instead, consider returning the values you need to keep track of as composite values. An easy data type to start with would be a tuple:
let ``first part`` id =
let someOtherValue = "Foo"
someOtherValue, id + 1
This function takes an int (the current ID) as input, and returns string * int (a tuple where the first element is a string, and the second element and int) as output.
You can call it like this:
> let other, newId = ``first part`` 42;;
val other : string = "Foo"
val newId : int = 43
Notice that you can use pattern matching to immediately destructure the values into two named symbols: other and newId.
Your second function could also take an ID as input:
let ``second part`` id otherArgument =
// use id here, if you need it
"Bar"
You can call it like this, with the newId value from above:
> let result = ``second part`` newId "Baz";;
val result : string = "Bar"
If you find yourself doing this a lot, you can define a record for the purpose:
type Identifiable<'a> = { Id : int; Value : 'a }
Now you can begin to define higher-order functions to deal with such a type, such as e.g. a map function:
module Identifiable =
let map f x = { Id = x.Id; Value = f x.Value }
// Other functions go here...
This is a function that maps the Value of an Identifiable from one value to another, but preserves the identity.
Here's a simple example of using it:
> let original = { Id = 42; Value = "1337" };;
val original : Identifiable<string> = {Id = 42;
Value = "1337";}
> let result' = original |> Identifiable.map System.Int32.Parse;;
val result' : Identifiable<int> = {Id = 42;
Value = 1337;}
As you can see, it preserves the value 42, but changes the Value from a string to an int.
You can still change the ID explicitly, if you want to do that:
> let result'' = { result' with Id = 7 };;
val result'' : Identifiable<int> = {Id = 7;
Value = 1337;}
Since this was getting out of hand for comments this is how I would do it for an example
let mutable t = 0
let first =
t <- 1 + 1
//other stuff
let second =
//can use t here and it will have a value of 2
In some cases you have to use a ref:
let t = ref 0
let first =
t := 1 + 1
//other stuff
let second =
//can use t here and it will have a value of 2 -
// you use "!t" to get the value
If you define ident at the top of your file like this :
let ident = "foo"
// rest of your code using ident
ident are global and you can use in the next part of your file.
EDIT :
If ident wil change in the next part of your code, use this :
let ident = ref "foo"

creating sequences in ML

datatype 'a seq = Nil | Cons of 'a * (unit -> 'a seq);
fun head(Cons(x,_)) = x | head Nil = raise EmptySeq;
fun tail(Cons(_,xf)) = xf() | tail Nil = raise EmptySeq;
datatype direction = Back | Forward;
datatype 'a bseq = bNil
| bCons of 'a * (direction -> 'a bseq);
fun bHead(bCons(x,_)) = x | bHead bNil = raise EmptySeq;
fun bForward(bCons(_,xf)) = xf(Forward) | bForward bNil = raise EmptySeq;
fun bBack(bCons(_,xf)) = xf(Back) | bBack bNil = raise EmptySeq;
so after all those definitions. here's what I'm trying to do. I need to take 2 sequences and make them into 1 sequence that I can move Farward and Back on.
for example if 1 sequence is 0123... and 2 is -1-2-3-4..., I get -4-3-2-10123.
my current location must always be the first element of the sequence going "up".
this is what I've tried to do:
(*************************************************************)
local
fun append_aux (Nil, yq) = yq
|append_aux (Cons(x,xf), yq) = Cons(x,fn() => append_aux(xf(),yq))
fun append(t,yq) = append_aux(Cons(t,fn() => Nil),yq)
in
fun seq2bseq (Cons(x,xrev)) (Cons(y,ynorm)) = bCons(y,fn Farward => seq2bseq (append(y,Cons(x,xrev))) (ynorm())
|Back => seq2bseq (xrev()) (append(x,Cons(y,ynorm))))
|seq2bseq (_) (_) = bNil
end;
but I get an error "match redundant" for the "Back" match and I can't figure out why.