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

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

Related

Argument of type "str | None" cannot be assigned to parameter "__x" of type "ReadableBuffer | str"

I am getting an error when I access the value of a TextField to perform a sum operation with that number.
My code is as follows:
txt_number = ft.TextField(value="1", text_align=ft.TextAlign(value="center"), width=100)
def minus_click(e):
txt_number.value = str(int(txt_number.value) - 1)
page.update()
the error I get in the console is the following:
Argument of type "str | None" cannot be assigned to parameter "__x" of type "ReadableBuffer | str | SupportsInt | SupportsIndex | SupportsTrunc" in function "new"
  Type "str | None" cannot be assigned to type "ReadableBuffer | str | SupportsInt | SupportsIndex | SupportsTrunc"
    Type "None" cannot be assigned to type "ReadableBuffer | str | SupportsInt | SupportsIndex | SupportsTrunc"
      Type "None" cannot be assigned to type "str"
      Type "None" cannot be assigned to type "ReadOnlyBuffer"
      Type "None" cannot be assigned to type "bytearray"
      Type "None" cannot be assigned to type "memoryview"
      Type "None" cannot be assigned to type "array[Any]"
      Type "None" cannot be assigned to type "mmap"
...
I tried the example from Flet's counter documentation and got the same error. I even copied and pasted just the example and it still sent that problem in the console.
I have already checked that there was no error when trying to cast between a str and an integer. I think it is a Flet error.

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?" }

Get constructor by value option in Elm (0.19.1)

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

Can't get record property of a record

I have this
{ a |
b = { a.b |
c =
Utils.newC
a.b.c
}
}
But the compiler just says "no" :
-- SYNTAX PROBLEM ----------------------------------------------------- Main.elm
I ran into something unexpected when parsing your code!
43| b = { a.b |
^
I am looking for one of the following things:
"'"
"|"
an equals sign '='
more letters in this name
whitespace
I don't know what to do now. How to get a with c property of b changed to a new value?
Updating nested records is a little more verbose in Elm than in other languages, and the syntax of { a.b | ... } updates is not allowed. Here is an alternative:
let
b = a.b
newB = { b | c = Utils.newC b.c }
in
{ a | b = newB }
See this related question for more information on standard ways of updating nested record values in Elm.

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.