How does SML pattern matching records work - functional-programming

Disclaimer, this is part of an online course, but I have solved the homework. My question is about the pattern matching of records work?
So in the first function I do not have to specify the structure of the record
fun generateName (ls , name)=
case ls of
[] => name::[]
| x::xs =>
{
first= x,
middle= #middle name,
last= #last name
}
:: generateName(xs, name);
In this function I do. Otherwise I get a flex record error.
fun createName (f, name :{first:string, middle:string, last:string}) =
case f of
x =>
{
first= x,
middle= #middle name,
last= #last name
}
Even more confusingly this one gives no error
fun generateName2 (nms, name) =
let fun aux (ls, name, acc) =
case ls of
[] => name::acc
| x::xs =>
aux(xs, name,
{
first= x,
middle= #middle name,
last= #last name
} :: acc)
in
aux (nms, name, [])
end
When do I have to specify the record fields?

SML does not have row polymorphism, so the compiler needs to know all field names of a record.
In the functions that return lists, the recursion limits the possible types; if name has the type R, generateName (ls , name) has the type R list, and all the field names can be inferred from the non-empty recursive case.
The middle example does not have such a relationship between input and output, so unless you specify the input field names, SML does not know what they are.

The answer from #molbdnilois quite good. Because I firmly believe that fully taking advantage of the niceties of programming languages can make for code that's easier to reason about, let's clean up your code examples a bit. In theory this belongs in a comment, but that's not very well-suited to this level of commentary.
I hope the ideas shown here can help you. Good learning!
fun generateName (ls , name)=
case ls of
[] => name::[]
| x::xs =>
{
first= x,
middle= #middle name,
last= #last name
}
:: generateName(xs, name);
When we see a case as the first thing in a function body, and it's simply acting on one of the arguments, we can use pattern matching in the fucntion signature itself.
fun generateName([] , name) = name :: []
| generateName(x::xs, name) =
{ first = x,
middle = #middle name,
last = #last name } :: generateName(xs, name);
We can also use pattern-matching further to eliminate the need to extract the record fields inside the function body.
fun generateName([] , name) = name :: []
| generateName(x::xs, name as {first:string, middle:string, last:string}) =
{ first = x,
middle = middle,
last = last } :: generateName(xs, name);
In your second example, pattern matching an argument with just one catch-all pattern is pointless. We can get rid of that.
fun createName (f, name :{first:string, middle:string, last:string}) =
{ first = f,
middle = #middle name,
last = #last name }
And we don't have to specify the type, if we offer it up as a pattern.
fun createName (f, {first:string, middle:string, last:string}) =
{ first = f,
middle = middle,
last = last }
Your third code sample can benefit from these ideas as well.
fun generateName2(nms, name) =
let
fun aux([], name, acc) = name :: acc
| aux(x::xs, name as { first:string, middle:string, last:string }, acc) =
aux(xs, name,
{ first = x,
middle = middle,
last = last } :: acc)
in
aux(nms, name, [])
end

Related

F# (F sharp) unzip function explained

I'm taking a university course in functional programming, using F#, and I keep getting confused about the logical flow of the following program. Would anyone care to explain?
let rec unzip = function
| [] -> ([],[])
| (x,y)::rest ->
let (xs,ys) = unzip rest
(x::xs,y:ys);;
So this program is supposed to take a list of pairs, and output a pair of lists.
[(1,'a');(2,'b')] -> ([1;2],['a','b'])
It seems to me, like the base case where the argument (list) is empty, the format of the output is given, but I don't understand how the third and fourth line is evaluated.
let (xs,ys) = unzip rest
(x::xs,y:ys);;
Firstly, this is a recursive function - the rec keyword is a giveawy :).
These can be quite hard to get you head around, but are quite common in functional programming.
I'll assume you are OK with most of the pattern matching going on, and that you are aware of the function keyword shorthand.
let rec unzip = function
| [] -> ([],[])
| (x,y)::rest ->
let (xs,ys) = unzip rest
(x::xs,y:ys);;
You seem quite happy with:
| [] -> ([],[])
Given an empty list, return a tuple with 2 empty lists. This isn't just a guard clause, it will be used later to stop the recursive program running forever.
The next bit...
| (x,y)::rest ->
Takes the first element (head) of the list and splits it off from the tail. It also deconstructs the head element which is a tuple into 2 values x and y.
The could be written out long hand as:
| head::rest ->
let x,y = head
Now is the fun part where it calls itself:
let (xs,ys) = unzip rest
(x::xs,y:ys);;
It might help to walk though an example an look at what goes on at each step:
unzip [(1,'a');(2,'b');(3,'c')]
x = 1
y = 'a'
rest = [(2,'b'); (3,'c')]
unzip rest
x = 2
y = 'b'
rest = [(3,'c')]
unzip rest
x = 3
y = 'c'
rest = []
unzip rest
return [],[]
xs = []
ys = []
return [x:xs],[y:ys] # 3:[] = [3], 'c':[] = ['c']
xs = [3]
ys = ['b']
return [x:xs],[y:ys] # 2:[3] = [2,3], 'b':['c'] = ['b', 'c']
xs = [2,3]
ys = ['b','c']
return [x:xs],[y:ys] # 1:[2;3] = [1,2,3], ['a']:['b';'c'] = ['a', 'b', 'c']
done

Split list into 2 lists of odd & even positions - SML?

I'm required to write a function that takes a list and splits it into 2 lists. The first list will hold elements in odd position and 2nd list hold elements in even position. Here's my attempt which gives me the following warning:
Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)
How to improve this?
fun splt (lst: int list) =
let
fun splt2 (lst: int list, count: int, lst1: int list, lst2: int list) =
if null lst
then []
else if (count mod 2 = 0)
then splt2 (tl lst, count+1, hd lst::lst1, lst2)
else splt2 (tl lst, count+1, lst1, hd lst::lst2)
in
splt2 (lst,1,[],[])
end
Here's a 2nd correct implementation that I found but I'm mainly interested in fixing the 1st one!!
I want to split a list into a tupple of odd and even elements
fun split [] = ([], [])
| split [x] = ([x], [])
| split (x1::x2::xs) =
let
val (ys, zs) = split xs
in
((x1::ys), (x2::zs))
end;
UPDATE: Improvement is just replace
if null lst then
[]
with this:
if null lst then
[lst1]#[lst2]
Here's some feedback for your code:
Give the function a proper name, like split or partition. The connotations that I have for those names are: Splitting (or exploding) takes something and returns one list of sub-components (e.g. string → char list), while partitioning takes a list of something and divides into two based on a predicate (e.g. List.partition), but they're not really set in stone.
Make up some variable names that aren't lst, since this is just an abbreviation of the type - surely redundant when even the type is there. For generic methods, good names can be hard to come by. A lot of ML code uses stuff like xs to imply a generic, plural form.
Ditch the type annotations; you'll get a polymorphic function that reads more easily:
fun split input =
let
fun split' (xys, count, xs, ys) = ...
in
split' (input, 1, [], [])
end
But really, the version you found online has some advantages: Pattern matching ensures that your lists have the right form before the function body is triggered, which minimizes run-time bugs. The functions hd and tl don't.
You could optimize the order of the cases slightly; i.e. list the most common case first. The parenthesis around x::xs and y::ys is unnecessary. Also, the two other cases (of one or zero elements) can be combined for brevity, but it doesn't matter much.
fun split (x1::x2::xs) =
let
val (ys, zs) = split xs
in
(x1::ys, x2::zs)
end
| split rest = (rest, [])
You could also use case-of instead of let-in-end:
fun split (x1::x2::xs) =
(case split xs of
(ys, zs) => (x1::ys, x2::zs))
| split rest = (rest, [])
Lastly, you may want to make this function tail-recursive:
fun split xys =
let fun split' (x1::x2::xs, ys, zs) = split' (xs, x1::ys, x2::zs)
| split' (rest, ys, zs) = (rev (rest # ys), rev zs)
in
split' (xys, [], [])
end
To help get you over the error you are encountering
you need to look at the type of the function which you have given
val splt = fn : int list -> 'a list
and ask yourself what does an 'a list hold?
- val foo = "foo"::(splt[1,2,3,4,5]);
val foo = ["foo"] : string list
- val bar = 52::splt[1,2,3,4,5];
val bar = [52] : int list
it can hold anything, but the compiler cannot tell by itself.

F# compiler complaining of invalid type in recursive function

In F# I am trying to write a function that given two strings it will return all indexes of the start of the second string in the first string. My function looks like this:
let allIndexOf (str:string) (c:string) =
let rec inner (s:string) l =
match (s.IndexOf(c), (s.IndexOf(c)+1) = s.Length) with
| (-1, _) -> l
| (x, true) -> x::l
| (x, false) -> inner(s.Substring(x+1) x::l)
inner str []
The problem is on the line (x, false) -> inner(s.Substring(x+1) x::l) the compiler says expected type int list but got int list -> int list. What am I doing wrong here?
In this case I want to call inner with the rest of the string (minus the part where it matched) to look for more matches.
Did you forget the parens between the first and the second argument?
| (x, false) -> inner (s.Substring(x+1)) (x::l)

How to clean a list for a specific string and return the cleaned list or NONE

I need to write a function to answer these specifications:
clean_list( [],s1] = NONE
clean_list( xs, "") = NONE
clean_list ([s1, s1, s1, s1], s1) = NONE
clean_list([s1, s2, s3, s2, s1], s3) = [s1, s2, s2, s1]
where s1, s2, s3 are some strings and xs a list of strings.
I was able to do it using the two helper functions is_into(xs: string list, s1: string) -> bool and remove(xs: string list, s1: string) -> string list but it seems ugly to recurse twice through the list.
clean_list(xs: string list, s1: string) =
case (xs, s1) of
( [], _ ) => NONE
|( _, "" ) => NONE
|( _, _) => if is_into(xs, s1)
then remove(xs, s1)
else NONE
Is there a way to do it without recursing in the list twice (once in is_into and once in remove)?
Note: None of builtin functions are to be used.
Sorry i forgot an important case in the specifications
clean_list ([s1, s2, s3, s4], s10] = NONE
You can easily go through the list once, element by element and removing all that matches the given string, returning SOME lst at the end, where lst is the resulting list
fun clean_list ([], _ ) = NONE
| clean_list (_, "") = NONE
| clean_list (xs, str) =
let
fun clean_list' [] = []
| clean_list' (x::xs) =
if x = str then
clean_list' xs
else
x :: clean_list' xs
in
SOME (clean_list' xs)
end
Update
I noticed that the above code actually doesn't handle the case: clean_list ([s1, s1, s1, s1], s1) = NONE. However this is an easy fix.
I can see that given your new updated specification, that if the element is not in the list in the first place, then NONE should be returned. This is the same as saying, if no elements was removed while going through all the elements, then return NONE.
Hopefully you can see that this can be implemented by added an extra Boolean argument to the helper function, initially setting it to false, and then passing along its value in each recursive call, except in the case where an element is removed, here it can always be set to true.
This can then be used to determine weather to return the SOME lst, where lst is the resulting list, or NONE if no elements was removed.
Given these two things, that needs to be fixed, it would probably bee a good idea to have the helper function built up its result in an accumulating argument, instead. This way you have full control and can easily return NONE when the accumulating list is empty at the end.
Your clean_list function doesn't typecheck since some branches have type 'a option and others have type 'b list.
Assuming you would like to return 'b list, NONE should be replaced by []. You can use pattern matching recursively on clean_list to avoid the need of auxiliary functions:
fun clean_list(xs: string list, s1: string) =
case (xs, s1) of
([], _) => []
| (_, "") => []
| (x::xs', _) => if x = s1
then clean_list(xs', s1)
else x::clean_list(xs', s1)

Embed a variable inside an F# quotation

I'm writing an F# dsl for SQL (http://github.com/kolosy/furious).
A select statement would look like this:
type person = {
personId: string
firstname: string
lastname: string
homeAddress: address
workAddress: address
altAddresses: address seq
}
and address = {
addressId: string
street1: string
zip: string
}
let (neighbor: person seq) =
db.Yield <# Seq.filter (fun p -> p.homeAddress.zip = '60614') #>
The obvious (and silly) question is... How do I parametrize the quotation?
If I just somehting like:
let z = "60614"
let (neighbor: person seq) =
db.Yield <# Seq.filter (fun p -> p.homeAddress.zip = z) #>
then z gets resolved into a static property accessor (PropertyGet(None, String z, [])). I need something that will let me retrieve the value of the variable/let binding based solely on the quotation. Ideas?
Quotations are not my forte, but check out the difference here:
let z = "60614"
let foo = <# List.filter (fun s -> s = z) #>
printfn "%A" foo
let foo2 =
let z = z
<# List.filter (fun s -> s = z) #>
printfn "%A" foo2
I think maybe having 'z' be local to the expression means the value is captured, rather than a property reference.
In addition to what Brian wrote - I believe that the encoding of access to global let bound values is also pretty stable and they will quite likely continue to be encoded as PropGet in the future.
This means that you could support this case explicitly in your translator and add a simple pre-processing step to get values of these properties. This can be done using ExprShape (which allows you to fully traverse quotation just using 4 cases). This would allow your DSL to support the general case as well.
The following function traverses quotation and replaces access to global lets with their value:
open Microsoft.FSharp.Quotations
let rec expand e =
match e with
// Extract value of global 'let' bound symbols
| Patterns.PropertyGet(None, pi, []) ->
Expr.Value(pi.GetValue(null, [| |]), e.Type)
// standard recursive processing of quotations
| ExprShape.ShapeCombination(a, b) ->
ExprShape.RebuildShapeCombination(a, b |> List.map expand)
| ExprShape.ShapeLambda(v, b) -> Expr.Lambda(v, expand b)
| ExprShape.ShapeVar(v) -> Expr.Var(v)
Then you can write the following to get a quotation that contains value instead of PropGet:
let z = 5
let eOrig = <# Seq.filter (fun p -> p = z) [ 1 .. 10 ]#>
let eNice = expand eOrig

Resources