Range expressions in Elm - elm

Elm supports [1..100], but if I try ['a'..'z'], the compiler gives me a type mismatch (expects a number, gets a Char). Is there any way do make this work?

Just create a range of numbers and map it to chars:
List.map Char.fromCode [97..122]
Edit, or as a function:
charRange : Char -> Char -> List Char
charRange from to =
List.map Char.fromCode [(Char.toCode from)..(Char.toCode to)]
charRange 'a' 'd' -- ['a','b','c','d'] : List Char
Edit, from elm 0.18 and up, List.range is finally a function:
charRange : Char -> Char -> List Char
charRange from to =
List.map Char.fromCode <| List.range (Char.toCode from) (Char.toCode to)

Related

Printf arguments in OCaml

In OCaml, I obtain an error I do not understand when passing arguments which should work to Printf.printf. It is probably because I do not understand that function completely, but I cannot pin down what does not work.
First, I define a function (used for logging):
utop # let log verbosity level str =
if level <= verbosity then (
Printf.printf "\nLevel %i: " level;
Printf.printf str);;
val log : int -> int -> (unit, out_channel, unit) format -> unit = <fun>
All seems well, but then I obtain this:
utop # log 0 0 "%i" 0;;
Error: This function has type
int -> int -> (unit, out_channel, unit) format -> unit
It is applied to too many arguments; maybe you forgot a `;'.
although the following works:
utop # Printf.printf;;
- : ('a, out_channel, unit) format -> 'a = <fun>
utop # Printf.printf "%i" 0;;
0- : unit = ()
So, how can I define a function which does what log intends to do ?
Edit: Indeed, log 0 0 "%i" 0;; looks like too many arguments (4 instead of 3), but so does Printf.printf "%i" 0;; (2 instead of 1), and it works. With partial application, this gives this:
utop # Printf.printf "%i";;
- : int -> unit = <fun>
utop # log 0 0 "%i";;
Error: This expression has type (unit, unit) CamlinternalFormatBasics.precision
but an expression was expected of type
(unit, int -> 'a) CamlinternalFormatBasics.precision
Type unit is not compatible with type int -> 'a
The printf-like functions are variadic, in the sense that they accept a variable number of arguments. It is not really specific to printf and the family, you can define your own variadic functions in OCaml, this is fully supported by the type system. The only magic of printf is that the compiler translates a string literal, e.g., "foo %d to a value of type format.
Now, let's look at the type of the printf function,
('a, out_channel, unit) format -> 'a
Notice that it returns 'a which is a type variable. Since 'a could be anything it could be also a function. The ('a, out_channel, unit) format is the type of the format string that defines the type of function that is generated by this format string. It is important to understand though, that despite that "foo %d" looks like a string, in fact, it is a special built-in value of type _ format, which has a literal that looks like a string (though not all valid strings are valid literals for the _ format type.
Just to demonstrate that the first argument of printf is not a string, let's try the following,
# Printf.printf ("foo " ^ "%d");;
Line 1, characters 14-29:
1 | Printf.printf ("foo " ^ "%d");;
^^^^^^^^^^^^^^^
Error: This expression has type string but an expression was expected of type
('a, out_channel, unit) format
Now, when we know that printf is not a typical function, let's define a printf-like function ourselves. For that we need to use the kprintf-family of functions, e.g.,
# #show Printf.ksprintf;;
val ksprintf : (string -> 'd) -> ('a, unit, string, 'd) format4 -> 'a
This function takes the function which receives the resulting string which we can log, for example,
# let log fmt = Printf.ksprintf (fun s -> print_endline ("log> "^s)) fmt;;
val log : ('a, unit, string, unit) format4 -> 'a = <fun>
# log "foo";;
log> foo
- : unit = ()
This resulting function looks more like sprintf, i.e., it will play nicely with pretty-printing function that work with string as their output devices (this is a different topic). You may find it more easier to define your logging functions, using either Printf.kfprintf or, much better, using Format.kasprintf or Format.kfprintf. The latter two functions have the following types,
val kasprintf : (string -> 'a) -> ('b, formatter, unit, 'a) format4 -> 'b
val kfprintf : (formatter -> 'a) -> formatter ->
('b, formatter, unit, 'a) format4 -> 'b
But the type of format works with the formatter type (which is an abstraction of the output device) that is the type that the pretty printers (conventionally named pp) are accepting. So the log function defined using the Format module will play better with the existing libraries.
So, using Format.kasprintf we can define your log function as,
# let log verbosity level =
Format.kasprintf (fun msg ->
if level <= verbosity then
Format.printf "Level %d: %s#\n%!" level msg);;
val log : int -> int -> ('a, Format.formatter, unit, unit) format4 -> 'a = <fun>
And here is how it could be used,
# log 0 0 "Hello, %s, %d times" "world" 3;;
Level 0: Hello, world, 3 times
- : unit = ()

Elm: How can I find out the type of an Elm expression or a subexpression in elm-repl?

How can I find out the type of an Elm expression or a subexpression in elm-repl ?
Haskell's :type or :t equivalent in Elm REPL?
The Elm REPL automatically prints the type of whatever you enter. For example:
> "foo"
"foo" : String
> f = \a b c -> (a + 1, b ++ "!", c || False)
<function> : number -> String -> Bool -> ( number, String, Bool )
> f
<function> : number -> String -> Bool -> ( number, String, Bool )
> f2 a b c = (a + 1, b ++ "!", c || False)
<function> : number -> String -> Bool -> ( number, String, Bool )
As #amalloy points out, without an equivalent to GHCi's :type command, Elm REPL (as of 0.18) forces evaluation of an expression prior to showing you the type, which may be undesirable for expensive function calls. In its current version, there is no way around this.

How do I build a HMAC code in Elm?

I'm trying to implement HMAC in Elm,
but I can't seem to figure out what I'm doing wrong here.
Some help would be greatly appreciated 🙏
type alias HashFunction =
String -> String
encrypt64 : HashFunction -> String -> String -> String
encrypt64 =
encrypt 64
encrypt : Int -> HashFunction -> String -> String -> String
encrypt blockSize hasher message key =
let
keySize =
String.length key
keyWithCorrectSize =
if keySize > blockSize then
hexStringToUtf8String (hasher key)
else if keySize < blockSize then
String.padRight blockSize (Char.fromCode 0) key
else
key
keyCodePoints =
keyWithCorrectSize
|> String.toList
|> List.map Char.toCode
partA =
keyCodePoints
|> List.map (Bitwise.xor 54 >> Char.fromCode)
|> String.fromList
partB =
keyCodePoints
|> List.map (Bitwise.xor 92 >> Char.fromCode)
|> String.fromList
in
message
|> String.append partA
|> hasher
|> hexStringToUtf8String
|> String.append partB
|> hasher
-- Utils
hexStringToUtf8String : String -> String
hexStringToUtf8String input =
input
|> String.toList
|> List.Extra.greedyGroupsOf 2
|> List.map (String.fromList >> hexStringToUtf8Char)
|> String.fromList
hexStringToUtf8Char : String -> Char
hexStringToUtf8Char input =
case Hex.fromString input of
Ok v ->
Char.fromCode v
Err err ->
Debug.crash err
You can find the related code here: https://github.com/icidasset/ongaku-ryoho/blob/master/src/App/Sources/Crypto/Hmac.elm (which includes doc tests)
edit: To be more clear... my current code here doesn't output a valid HMAC,
and I would like to know why.
Looking at the Elm SHA library, I think the problem (or at least a problem) is that the output from the hash is hex encoded. HMAC calls the hash function twice, feeding the first output back into the second call, and this needs to be the raw SHA bytes, rather than a hex string.
So you need to decode the hex output from the first call to hasher, after it is applied to partA.

Compare char to string in Elm

I'd like to loop through the characters of a string and see if each is contained in another string. However, String.toList returns a list of Chars, not Strings, which isn't allowed by the String.contains function. Passing the Char to toString doesn't help, is there another way to achieve this goal, or do I just need another approach altogether?
> String.contains (toString 'a') "asdf"
False : Bool
Use String.fromChar to convert a character to string.
String.fromChar 'a' -- "a"
String.contains (String.fromChar 'a') "asdf" -- True
Use lists of characters directly:
> standard = String.toList "asdf"
['a','s','d','f'] : List Char
> candidateGood = String.toList "asd"
['a','s','d'] : List Char
> candidateBad = String.toList "abc"
['a','b','c'] : List Char
> List.all (\x -> List.member x standard) candidateGood
True : Bool
> List.all (\x -> List.member x standard) candidateBad
False : Bool

String to int conversion using toInt function

I am trying to convert a string to integer using String.toInt. However, when I want to bind the result to a variable and then do some simple math with it I get this error:
Function add is expecting the 2nd argument to be:
Int
But it is:
Result String Int
How can I just extract the integer part of the result?
Here's how to supply the conversion with a default value in case the parsing fails.
String.toInt "5" |> Result.toMaybe |> Maybe.withDefault 0
toInt can fail in parsing. You need to check it using a case statement:
case toInt str of
Err msg -> ... -- do something with the error message
Ok val -> ... -- val is an Int which you can add
More about Result here
The integer can also be pulled out using
Result.withDefault 0 (String.toInt "2")
You can read more about it here
According to the Elm String reference documentation, if you are extracting a number from some raw user input, you will typically want to use Result.withDefault to handle bad data in case parsing fails. You can chain this operation using pipes for cleaner code:
String.toInt "5" |> Result.withDefault 0
Maybe.withDefault 0 (String.toInt "42")
Use map:
answer = Result.map2 (+) (String.toInt "1") (String.toInt "2")
map2:
Apply a function to two results, if both results are Ok. If not, the
first argument which is an Err will propagate through.
to have the add result as a string
resultAsString r =
case r of
Err msg -> msg
Ok value -> toString value
resultAsString answer
to make things easier you can create an addStrings function:
addStrings : String -> String -> Result String Int
addStrings a b =
Result.map2 (+) (String.toInt a) (String.toInt b)
You can even get away with the Result type altogether:
addStrings : String -> String -> String
addStrings a b =
let
r =
Result.map2 (+) (String.toInt a) (String.toInt b)
in
case r of
Err msg ->
msg
Ok value ->
toString value
Testing
import Html exposing (Html, text)
main : Html msg
main =
text (addStrings "1" "2")
output 3
The withDefault method forces you to define a value that can be used for calculations but it is not always possible to establish a value that is significant for errors. Most often you need all the possible values, and default is not fit. Here I provide a result type check function you can use to decide if you use or not the converted value:
isErrorResult r =
case r of
Err msg ->
True
Ok value ->
False
You can use it like this:
r = String.toInt "20b"
if isErrorResult r then
-- not a valid Interger, abort or whatever
else
-- a good integer, extract it
a = Result.withDefault 0 r
-- and make good use of it
the default value (0 in this case) passed to withDefault is meaningless, because we made sure that r is not an Err.
You can do this as below.
---- Elm 0.19.0 ----------------------------------------------------------------
Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
--------------------------------------------------------------------------------
> parseInt string = String.toInt string
<function> : String -> Maybe Int
> resultParseInt string = \
| Result.fromMaybe ("error parsing string: " ++ string) (parseInt string)
<function> : String -> Result String Int
> resultParseInt "12"
Ok 12 : Result String Int
> resultParseInt "12ll"
Err ("error parsing string: 12ll") : Result String Int
>
I have changed guys answers a bit, since it appears to be of type Maybe
isErrorResult : String -> Bool
isErrorResult r =
case String.toInt r of
Nothing -> True
Just value -> False