OCaml: creating a record from a function - ocaml

type node = {
lan: string;
lat: string;
};;
let rec read_nodes_from_list list = match list with
| Xml.Element("node", _, _)::list' -> {lan="A"; lat="B"}::read_nodes_from_list list'
;;
I tried this to create a node record but it doesn't work. And suppose I have another type that has same attributes of node, how can I tell ocaml which type object to create?
Thank you.

Obviously, your function didn't work because you forgot to match with empty list:
let rec read_nodes_from_list list = match list with
| Xml.Element("node", _, _)::list' -> {lan="A"; lat="B"}::read_nodes_from_list list'
| [] -> []
What you're actually trying to do is a map operation on list, so your function could be written more elegantly as follows:
let read_nodes_from_list list =
List.map (fun (Xml.Element("node", _, _)) -> {lan="A"; lat="B"}) list
However, the function may not work because pattern matching on Xml.Element is not exhaustive. You should be careful when handling remaining cases. For example, something like this would work:
let read_nodes_from_list list =
List.map (function | (Xml.Element("node", _, _)) -> {lan="A"; lat="B"}
| _ -> {lan=""; lat=""}) list
To answer your question about record types, it considers a bad practice to have two record types with the same field label. You still can put these record types in different submodules and distinguish them using module prefixes. But as I said, having two similar record types in the same module causes confusion to you and the OCaml compiler.

Related

How to use List.fold_left to populate keys of empty string map in ocaml?

I have an existing list, which I would like to move into a empty StringMap. I just want the items in this list to become the keys of said StringMap.
This is what I'm trying to do right now:
utop # List.fold_left StringMap.empty [1,2,3];;
Error: This expression has type 'a StringMap.t
but an expression was expected of type 'b -> 'c -> 'b
But clearly it's wrong as I'm getting the above error message
How should I go about this? Thanks in advance
List.fold_left takes three parameters: a function to be "folded", an initial value, and the list to be processed. You're only supplying two parameters. You need to supply a function that processes each element of the list and produces an intermediate result.
Here is an example that adds up all the elements of a list:
# List.fold_left (fun a b -> a + b) 0 [1;3;5;7];;
- : int = 16
You need a function like the one above except that it should add an element to the map.

F# If statement type mismatch

i am in a bit of a pickle, i have tried for 2 hours straight trying to get this code to work and im lost.
let DropColumn list =
if List.exists List.isEmpty list then "empty value"
else
list |> List.map List.tail
This gives me an error error FS0001: The type 'string' does not match the type ''a list'
The usual way to deal with failure in functional programming is to use the Result type, which is essentially defined as:
type Result<'T,'TError> =
| Ok of 'T
| Error of 'TError
To apply it to your code we can just wrap the string in the unhappy path with Error and the list from the happy path with Ok:
let DropColumn list =
if List.exists List.isEmpty list then
Error "empty value"
else
list |> List.map List.tail |> Ok
Then to use it you can use pattern matching:
match DropColumn myList with
| Ok newList ->
newList
| Error message ->
printfn "Error occurred: %s" message
[]
The answer given by #glennsl is correct and in many cases the preferred way. However, I'd like to add that there are two other common ways of dealing with invalid input:
Raise an exception. Use this for exceptional cases only, i.e. where you expect your code to halt as the result of invalid data. Do not use it for normal validation where you expect that data can often be wrong.
Use option. This is similar to using Result, but doesn't maintain information for the invalid case. This approach is very common and used a lot in library functions like List.tryFind, List.tryHead etc.
Raise an exception
In the comments you show you already know this option exists, but let's give it here for completeness:
let dropColumnOrRaise list =
if List.exists List.isEmpty list then failwith "empty value"
else
list |> List.map List.tail
Use option
This method usually requires that the business logic that shows an error or does recovery, goes elsewhere.
let tryDropColumn list =
if List.exists List.isEmpty list then None
else
list
|> List.map List.tail
|> Some
Use it as follows:
match tryDropColumn myCols with
| Some columns ->
// do something with valid columns, i.e., display them
printfn "%i columns remaining (List.length (List.head myCols))"
| None ->
// error recovery or showing a message
printfn "No column selected"
When you are dealing with several functions that operate on data that all return option, you can pipe them together with Option.bind (or Option.map if a function doesn't return option).
myCols
|> tryDropColumn
|> Option.map logColumns // function that always succeeds
|> Option.bind tryAtLeastTwoColumns // function that returns None on 1 or 0
|> Option.map showColumns
The code above removes the need to have a match x with for each returned option. Similar code can be used for Result from the previous answer.

Haskell List with tuples which can be extended - like a Dictionary

I am a beginner in Haskell and trying to learn it, so please excuse my obliviousness.
I am currently trying to implement a Telephone book, which is a List of tuples [(Name, Number)] (Both are Strings).
type TelephoneBook = [(String),(String)] (?)
However, I have no clue how I can extend this list by another tuple.
For example: [("Fred", "47/273")] and now I want to add another tuple.
I was trying to understand how the module dictionary works to see how I can extend this List and I stumbled upon "data" and "type".
An idea I had was to create a several types of this TelephonBook:
let a = TelephoneBook ("Fred","42/2321")
but that is just a simple idea... I am kinda lost on how to extend this list by another tuple, taking into account that once something is defined it can't be altered (or can it).
(Please don't give the solution to the Problem but simply an idea on how to start or what I should Research further)
The (:) operator prepends elements to lists. For example:
> ("John", "555-1212") : [("Fred", "42/2321")]
[("John","555-1212"),("Fred","42/2321")]
because you're asking to extend a list:
i have to disappoint you. That's not possible in Haskell. You can construct a new one. Out of one Element and another List.
the list type in Haskell is defined similar to:
-- 1 2 3 4
data [a] = a : [a] | []
-- 1: if you encounter the type [a]
-- 3: it is either
-- 2: an element `e` and a list `l` forming the new list `e:l`
-- 4: or an empty List `[]`
-- so the types of the constructors are:
-- (:) :: a -> [a] -> [a]
-- [] :: [a]
So having a new element and a list you can construct a new one, using (:)!
type Entry = (String, String)
type Book = [Entry]
addEntry :: Entry -> Book -> Book
addEntry e b = e : b -- this works, because book is just a list
-- without type aliases: (this is the same, but maybe slightly less nice to read)
addEntry' :: (String, String) -> [(String, String)] -> [(String, String)]
addEntry' e b = e : b
-- or even simpler:
addEntry'' = (:)
The type keyword in Haskell has to be understood as a type alias, so it's just another name for something, the representation in Haskell is the same.

OCaml Error involving lists

I'm still fairly new to OCaml, and would like some assistance on optimizing code.
I'm trying to multiply each element of a given list by the list's last element.
Here's a snippet of my code:
(* Find the last element of a function *)
let rec lastE = function
| [] -> []
| [x] -> x
| _ :: t -> lastE t;;
(*multiply a list by the last element*)
let rec lmul list =
match list with
[] -> []
| hd::tl -> (hd *. (lastE tl)) :: lmul tl;;
When I run the code I get this error message:
Error: This expression has type float list but
an expression was expected of type 'a list list
I'm been studying it for a while, but any assistance on this problem will be much appreciated.
To rephrase differently what Dave Newman is telling you, your basic problem is that lastE needs to handle an empty list differently. If lastE is supposed to return a number, it has to return a number in all cases. As it stands, lastE returns a list when it receives an empty list.
If you don't want to use List.map (again as Dave Newman suggests), you might at least consider calling lastE just once rather than once for each element of the list. This will make a big difference for long lists.

Haskell -- List Comprehension with Lists inside a Tuple inside a List

I have:
type Person = String
type Book = String
type Database = [(Person,[Book])]
I'm trying to define a function:
books :: Database -> Person -> [Book]
that takes in
1) a list of tuples (which contain a string and a list of strings
2) a String name
and returns a list of strings (namely the books from the database)
I want to use list comprehension, but I don't know how to access elements within a list that is in the tuple inside of the database list.
Thoughts?
Example database would look like:
db = [("Bob", ["Red Riding Hood", "Alice in Wonderland"]), ("Carol", ["Game of Thrones"])]
And if I ask for say "Carol", it should return ["Game of Thrones"].
Since you have an association list you can use the lookup function. It pretty much works exactly like you want:
getVal :: Database -> String -> Maybe [String]
getVal = lookup
The only difference is that it returns a Maybe but IMHO that is the right behavior, consider what would happen if you didn't have a value in your database when you looked it up?
Since you want to use pattern matching here is the source of lookup
lookup :: (Eq a) => a -> [(a,b)] -> Maybe b
lookup _key [] = Nothing
lookup key ((x,y):xys)
| key == x = Just y
| otherwise = lookup key xys
Or in your case
getVal :: Database -> String -> [String] --Avoids a Maybe if you want
getVal _key [] = []
getVal key ((x,y):xys)
| key == x = y
| otherwise = getVal key xys
As the other posters said, lookup is the way to go here: it's the standard procedure to search for something in an association list.
That said, a solution using a list comprehension would be
books :: Database -> Person -> [Book]
books db authorName = [book | (author, bookName) <- db,
author == authorName
book <- bookName]
It's taking out (author, bookName) tuples one by one, discarding the ones where the author doesn't match. If then adds the books of that author to the result.
Again, this implements a sort-of workaround for a function that's already in the standard libraries, and it's less readable in general. Really, use lookup.
I wouldn't use a list comprehension for implementing that function - list comprehensions are better suited for building lists.
What you are looking for is the Prelude function lookup:
lookup :: Eq a => a -> [(a, b)] -> Maybe b
As you can see it almost has the correct type, if a ~ Person and b ~ [Book] and after swapping the arguments. Since you want [Book] back and not Maybe [Book] you can wrap the whole thing inside fromMaybe, resulting in:
books db = fromMaybe [] . flip lookup db