OCaml dictionary update - ocaml

I am new to OCaml and am trying to learn how to update dictionary and deal with if/else conditionals.
I wrote the following code to check whether the dictionary has some key. If not, add a default value for that key. Finally print it out.
module MyUsers = Map.Make(String)
let myGraph = MyUsers.empty;;
let myGraph = MyUsers.add "test" "preset" myGraph in
try
let mapped = MyUsers.find "test" myGraph
with
Not_found -> let myGraph = MyUsers.add "test" "default" myGraph in
Printf.printf "value for the key is now %s\n" (MyUsers.find "test" myGraph)
The error message I have now is syntax error for line 6: with
What is wrong here? Also, when to use in ; or;; ?
I have done some google searches and understand that in seems to define some scope before the next ;;. But it is still very vague to me. Could you please explain it more clearly?

Your immediate problem is that, except at the top level, let must be followed by in. The expression looks like let variable = expression1 in expression2. The idea is that the given variable is bound to the value of expression1 in the body of expression2. You have a let with no in.
It's hard to answer your more general question. It's easier to work with some specific code and a specific question.
However, a semicolon ; is used to separate two values that you want to be evaluated in sequence. The first should have type unit (meaning that it doesn't have a useful value).
In my opinion, the double semicolon ;; is used only in the top-level to tell the interpreter that you're done typing and that it should evaluate what you've given it so far. Some people use ;; in actual OCaml code, but I do not.
Your code indicates strongly that you're thinking imperatively about OCaml maps. OCaml maps are immutable; that is, you can't change the value of a map. You can only produce a new map with different contents than the old one.

Related

terraform "element" and "concat" used together

we have a module that builds a security proxy that hosts an elasticsearch site using terraform. In its code there is this;
elastic_search_endpoint = "${element(concat(module.es_cluster.elasticsearch_endpoint, list("")),0)}"
which as I understand, then goes and finds the es_cluster module and gets the elasticsearch endpoint that was outputted from that. This then allows the proxy to have this endpoint available so it can run elasticsearch.
But I don't actually understand what this piece of code is doing and why the 'element' and 'concat' functions are there. Why can't it just be like this?
elastic_search_endpoint = "${module.es_cluster.elasticsearch_endpoint}"
Let's break this up and see what each part does.
It's not shown in the example, but I'm going to assume that module.es_cluster.elasticsearch_endpoint is an output value that is a list of eitehr zero or one ElasticSearch endpoints, presumably because that module allows disabling the generation of an ElasticSearch endpoint.
If so, that means that module.es_cluster.elasticsearch_endpoint would either be [] (empty list) or ["es.example.com"].
Let's consider the case where it's a one-element list first: concat(module.es_cluster.elasticsearch_endpoint, list("")) in that case will produce the list ["es.example.com", ""]. Then element(..., 0) will take the first element, giving "es.example.com" as the final result.
In the empty-list case, concat(module.es_cluster.elasticsearch_endpoint, list("")) produces the list [""]. Then element(..., 0) will take the first element, giving "" as the final result.
Given all of this, it seems like the intent of this expression is to either return the one ElasticSearch endpoint, if available, or to return an empty string as a placeholder if not.
I expect this is written this specific way because it was targeting an earlier version of the Terraform language which had fewer features. A different way to write this expression in current Terraform (v0.14 is current as of my writing this) would be:
elastic_search_endpoint = (
length(module.es_cluster.elasticsearch_endpoint) > 0 ? module.es_cluster.elasticsearch_endpoint : ""
)
It's awkward that this includes the full output reference twice though. That might be justification for using the concat approach even in modern Terraform, although arguably the intent wouldn't be so clear to a future reader:
elastic_search_endpoint = (
concat(module.es_cluster.elasticsearch_endpoint, "")[0]
)
Modern Terraform also includes the possibility of null values, so if I were writing a module like yours today I'd probably prefer to return a null rather than an empty string, in order to be clearer that it's representing the absense of a value:
elastic_search_endpoint = (
length(module.es_cluster.elasticsearch_endpoint) > 0 ? module.es_cluster.elasticsearch_endpoint : null
)
elastic_search_endpoint = (
concat(module.es_cluster.elasticsearch_endpoint, null)[0]
)
First things first: who wrote that code? Why is not documented? Ask the guy!
Just from that code... There's not much to do. I'd say that since concat expects two lists, module.es_cluster.elasticsearch_endpoint is a list(string). Also, depending on some variables, it might be empty. Concatenating an empty string will ensure that there's something at 0 position
So the whole ${element(concat(module.es_cluster.elasticsearch_endpoint, list("")),0)} could be translated to length(module.es_cluster.elasticsearch_endpoint) > 0 ? module.es_cluster.elasticsearch_endpoint[0] : "" (which IMHO is much readable)
Why can't it just be like this?
elastic_search_endpoint = "${module.es_cluster.elasticsearch_endpoint}"
Probably because elastic_search_endpoint is an string and, as mentioned before, module.es_cluster.elasticsearch_endpoint is a list(string). You should provide a default value in case the list is empty

Struggling to extract a section of a list in Haskell

I am trying to implement a basic function but I'm out of practice with Haskell and struggling so would really appreciate some help. My question is specifically how to select a section of a list by index. I know how to in other languages but have been struggling
[ x | x <- graph, x!! > 5 && x!! <10 ]
I have been fiddling around with basic list comprehension similar to what is above, and while I know that isn't right I was hoping a similarly simple solution would be available.
If anyone wants more information or felt like helping on the further question I have included more information below, thanks!
type Node = Int
type Branch = [Node]
type Graph= [Node]
next :: Branch -> Graph -> [Branch]
This is the individual question for the "next" function
This is the general set up information but most importantly that the graph is represented as a flattened adjacency matric
Apologies for the two pictures but it seemed the best way to convey the information.
As pointed out in the comments !! does not give you the index of a value in the way it seems you expect. It is just an infix for getting an element of a list.
There is no way to get the index of x like this in Haskell since the x object doesn't keep track of where it is.
To fix this we can make a list of objects that do keep track of where they were. This can be achieved with zip.
zip [0..] graph
This creates a list of tuples each containing their index and the value in graph.
So you can write your list comprehensions as
[ x | (index, x) <- zip [0..] graph, index > 5, index < 10 ]
Now this is not going to be terribly fast since it still needs to go through every element of the list despite the fact that we know no element after the 11th will be used. For speed we would want to use a combination of take and drop.
drop 5 (take 10 graph)
However if we wanted to do some other selections (e.g. all even indexes), we can still go back to the list comprehension.
In this case, you could drop 5 <&> take 4. As in drop 5 x & take 4. Drop skips the first few elements and take leaves out all but the first few left after the drop.

Modify current list by adding an element - Haskell 101

I'd like to add an element on the "movies" list of a data type Director variable called billy.
type Name = String
type Movie = String
data Director = Director {name:: Name, movies::[Movie]}
deriving (Show)
let billy = Director "Billy J." ["Good movie 1"]
--addMovieToDirector :: Movie -> Director -> Director
addMovieToDirector m (Director n ms) = Director n (m:ms)
The problem is previous function doesn't update billy's list of movies, it creates a new Director with the desired list (the changes are not stored on billy). How can I operate on billy's list without creating another Director? I understand, that Haskell works with constants, but then should I create a different 'billy' "variable" every time I modify the list?
Thanks!
What you would like to do can be described as "in-place modification", or "using mutable data".
There are ways for Haskell to do this. Since in-place modification of anything almost always considered as a "side-effect", such things can only be done in the IO monad, or with dirty tricks like unsafePerformIO.
These are somewhat advanced topics, and at a beginner level it is arguably beneficial to think about Haskell values as being totally immutable.
So yes, you can't modify variables. Actually there are no "variables" at all.
Think about billy as a name for a value, not a variable.
All a function can do in Haskell is to take arguments, and calculate some result without any side effects.
This is probably the biggest mental barrier for people coming from imperative languages: "how should I work with data if I can't modify it"?
The answer is: you should structure your program like giant assembly line: raw materials (raw data, initial parameters, etc.) are put on the line at the beginning (the first function you call), and each workstation (function) does something useful (returns a value), consuming the result of the previous workstation. At the end, something valuable might fall off the line.
What I described is simple function composition: if you need to do c task after b, after a, on a value x, then you can write it as (c . b . a) x, or c (b (a x)) or rather c $ b $ a x.
This way, you can write programs without ever changing anything explicitly, and only describing how to create new things out of old ones.
This sounds awfully inefficient, and indeed, there are some performance implications of functional programming (let alone laziness). However the compiler is smart enough to figure out a whole lot thing about programs written in Haskell, and optimize it in certain ways.
I hope it'll all make sense soon. :)
Oh, and welcome to Haskell. ;)
You can use a State monad if you want to have a mutable state in your program, for some reason. Here's an example:
module Main where
import Control.Monad.State
type GameValue = Int
type GameState = (Bool, Int)
type Name = String
type Movie = String
data Director = Director {name:: Name, movies::[Movie]}
deriving (Show)
addMovieToDirector :: Movie -> Director -> Director
addMovieToDirector m (Director n ms) = Director n (m:ms)
handleDirector :: Name -> State Director Director
handleDirector m = do
director <- get
put (addMovieToDirector m director)
returnDirector
returnDirector = do
director <- get
return director
startState = Director "Billy J." ["Good movie 1"]
main = print $ evalState (handleDirector "Good movie 2") startState
The printed result will be
Director {name = "Billy J.", movies = ["Good movie 2","Good movie 1"]}
Here handleDirector function of type Name -> State Director Director has a mutable state inside it of type Director and a "result" value of type, again, Director. get means get the state, put is used to change it and evalstate is used to "calculate" the result, enveloped by the constructed State monad.

concatenating a string onto all elements of [string] haskell

I'm new to haskell and can't seem to figure this out. I've been using the scalpel web scraping tool and want to concatenate a bunch of URL extensions with a URL.
For example, lets say we have scraped some URL extensions into a list of strings
result =["/contact","/content"]
and we have let
websiteURL = "www.website.com"
how do I arrive at the list ?
["www.website.com/contact", "www.website.com/content"]
map ("aaa"++) ["bbb", "ccc"]
==> [ "aaabbb", "aaaccc" ]
You want to traverse your list of extensions and apply a function to each, so some kind of map is required.
The function you want to apply is to append the websiteURL string, so the answer is:
map (mappend websiteURL) result
If you didn't know the mappend function, you could find it by searching hoogle for Monoid a => a -> a -> a.
(I'll let other people generalize to more abstract types if they want...)

Prolog Program for a recordings database

I have three types of facts:
album(code, artist, title, date).
songs(code, songlist).
musicians(code, list).
Example:
album(123, 'Rolling Stones', 'Beggars Banquet', 1968).
songs(123, ['Sympathy for the Devil', 'Street Fighting Man']).
musicians(123, [[vocals, 'Mick Jagger'], [guitar, 'Keith Richards', 'Brian Jones']].
I need to create these 4 rules:
together(X,Y) This succeeds if X and Y have played on the same album.
artistchain(X,Y) This succeeds if a chain of albums exists from X to Y;
two musicians are linked in the chain by 'together'.
role(X,Y) This succeeds if X had role Y (e.g. guitar) ever.
song(X,Y) This succeeds if artist X recorded song Y.
Any help?
I haven't been able to come up with much but for role(X,Y) I came up with:
role(X,Y) :- prole(X,Y,musicians(_,W)).
prole(X,Y,[[Y|[X|T]]|Z]).
prole(X,Y,[[Y|[H|T]]|Z]) :- prole(X,Y,[[Y|T]|Z]).
prole(X,Y,[A|Z]) :- prole(X,Y,Z).
But that doesn't work. It does work if I manually put in a list instead of musicians(_,W) like [[1,2,3],[4,5,6]].
Is there another way for me to insert the list as a variable?
As for the other rules I'm at a complete loss. Any help would really be appreciated.
You have a misconception about Prolog: Answering a goal in Prolog is not the same as calling a function!
E.g.: You expect that when "role(X,Y) :- prole(X,Y,musicians(_,W))." is executed, "musicians(_,W)" will be evaluated, because it is an argument to "prole". This is not how Prolog works. At each step, it attempts to unify the goal with a stored predicate, and all arguments are treaded either as variables or grounded terms.
The correct way to do it is:
role(X,Y) :- musicians(_, L), prole(X,Y,L).
The first goal unifies L with a list of musicians, and the second goal finds the role (assuming that the rest of your code is correct).
Little Bobby Tables is right, you need to understand the declarative style of Prolog. Your aim is to provide a set of rules that will match against the set of facts in the database.
Very simply, imagine that I have the following database
guitarist(keith).
guitarist(jim).
in_band('Rolling Stones', keith).
in_band('Rolling Stones', mick).
Supposed I want to find out who is both a guitarist and in the Rolling Stones. I could use a rule like this
stones_guitarist(X):-
guitarist(X),
in_band('Rolling Stones', X).
When a variable name is given within a rule (in this case X) it holds its value during the rule, so what we're saying is that the X which is a guitarist must also be the same X that is in a band called 'Rolling Stones'.
There are lots of possible ways for you to arrange the database. For example it might be easier if the names of the musicians were themselves a list (e.g. [guitar,[keith,brian]]).
I hope the following example for song(X,Y) is of some help. I'm using Sicstus Prolog so import the lists library to get 'member', but if you don't have that it's fairly easy to make it yourself.
:- use_module(library(lists)).
song(ARTIST,SONG):-
album(CODE,ARTIST,_,_),
songs(CODE,TRACKS),
member(SONG,TRACKS).