ML. "Unzipping" list of tuples. - error-handling

So I have this list of tuples(n=2), witch I'm supposed to "unzip" and by that create a new list like so: for list of tuples like this val it = (1,"one") :: (2,"two") :: nil : (int,string) alterlist, the unzip function will create a list like so [(1,2),("one", "two")].
This is what I got so far:
datatype ('a, 'b) alterlist = nil | :: of ('a*'b) * ('a, 'b) alterlist;
infixr 5 ::
fun build4(x, one, y, two) = (x,one)::((y,two)::nil);
fun unzip(alterlist) =
let
fun extract([], i) = []
| extract((x,y)::xs, i) = if i=0 then x::extract(xs, i)
else y::extract(xs, i);
in
(extract(alterlist, 0))::(extract(alterlist, 1))
end;
But I get a bunch of errors:
stdIn:48.6-50.26 Error: parameter or result constraints of clauses don't agree [tycon mismatch]
this clause: ('Z,'Y) alterlist * 'X -> 'W
previous clauses: 'V list * 'U -> 'W
in declaration:
extract =
(fn (nil,i) => nil
| (<pat> :: <pat>,i) =>
if <exp> = <exp> then <exp> :: <exp> else <exp> :: <exp>)
stdIn:49.41-49.58 Error: operator and operand don't agree [tycon mismatch]
operator domain: 'Z list * 'Y
operand: ('X,'W) alterlist * int
in expression:
extract (xs,i)
stdIn:50.9-50.26 Error: operator and operand don't agree [tycon mismatch]
operator domain: 'Z list * 'Y
operand: (_ * _,'X) alterlist * int
in expression:
extract (xs,i)
stdIn:48.6-50.26 Error: right-hand-side of clause doesn't agree with function result type [tycon mismatch]
expression: (_,_) alterlist
result type: 'Z list
in declaration:
extract =
(fn (nil,i) => nil
| (<pat> :: <pat>,i) =>
if <exp> = <exp> then <exp> :: <exp> else <exp> :: <exp>)
And being that I'm new to ml I have very little idea what causes it. Would greatly appreciate help!

My advice is first do it without the infix operator because it is a little bit confusing.
First solve it without and add it later.
Here is a solution without the infix:
datatype ('a,'b)alterlist = Nil
| element of 'a*('b,'a)alterlist;
fun unzip (Nil : ('a,'b)alterlist ) = ([],[])
| unzip (ablist : ('a,'b)alterlist) =
let
fun extract Nil = []
| extract (element (curr, Nil)) = curr::[]
| extract (element (curr, element(_,rest))) = curr::(extract rest)
val element (_, balist) = ablist
in
(extract ablist, extract balist)
end;
So for example a list like this: ["a", 1, "b", 2] would be created by:
element ("a", element (1, element ("b", element (2, Nil))));
Giving you:
val it = element ("a",element (1,element #)) : (string,int) alterlist
* The # is just to indicate the list is longer than is displayed.
And then if you would try unzip it;
You would get:
val it = (["a","b"],[1,2]) : string list * int list
As you would like.
Now try to change element into a :: infix operator.
Good luck!

Related

Extracting the year from a timestamp field inside an Esqueleto query

I am trying to make use of the unsafeSqlExtractSubField from Esqueleto to create one that will extract the year from a date, such as:
data ReportRow = ReportRow Text
userSignupData :: MonadIO m => Key User -> SqlPersistT m [ReportRow]
userSignupData _ = fmap toRow <$> select (from query)
where
query s =
pure $ (extractYear $ s ^. UserCreatedAt)
toRow yearVal = ReportRow (showText . unValue $ yearVal)
extractYear :: UnsafeSqlFunctionArgument a => a -> SqlExpr (Value Integer)
extractYear =
unsafeSqlExtractSubField "year"
showText :: Show a => a -> Text
showText = T.pack . show
But I am getting the error:
Could not deduce
(UnsafeSqlFunctionArgument
(expr0 (Value UTCTime)))
arising from a use of ‘query’
from the context: MonadIO m
bound by the type signature for:
userSignupData :: forall (m :: * -> *).
MonadIO m =>
Key User -> SqlPersistT m [ReportRow]
The type variable ‘expr0’ is ambiguous
|
20 | userSignupData _ = fmap toRow <$> select (from query)
| ^^^^^
Do I need to define an instance of UnsafeSqlFunctionArgument for UTCTime here or am I trying to fit a square peg into a round hole ?
I'm not after answers that state that I could extract the date at the haskell level, I'd like to get the year inside the query so that I can perform an SQL GROUP BY inside the query.

SML converting a string to an int with error catching

So what I want to do is to convert a string into an int and do some error catching on it. I would also like to know where I would put what I want it to do after it fails if it does.
I know how to convert, but I am not sure how to catch it and where the code will jump to after the error
I believe the method for converting it Int.fromString(x)
Thank you.
SML has two approaches to error handling. One, based on raise to raise errors and handle to catch the error, is somewhat similar to how error handling works in languages like Python or Java. It is effective, but the resulting code tends to lose some of its functional flavor. The other method is based on the notion of options. Since the return type of Int.fromString is
string -> int option
it makes the most sense to use the option-based approach.
An int option is either SOME n, where n is and integer, or it is NONE. The function Int.fromString returns the latter if it fails in its attempt to convert the string to an integer. The function which calls Int.fromString can explicitly test for NONE and use the valOf to extract the value in the case that the return value is of the form SOME n. Alternatively, and somewhat more idiomatically, you can use pattern matching in a case expression. Here is a toy example:
fun squareString s =
case Int.fromString(s) of
SOME n => Int.toString (n * n) |
NONE => s ^ " isn't an integer";
This function has type string -> string. Typical output:
- squareString "4";
val it = "16" : string
- squareString "Bob";
val it = "Bob isn't an integer" : string
Note that the clause which starts NONE => is basically an error handler. If the function that you are defining isn't able to handle such errors, it could pass the buck. For example:
fun squareString s =
case Int.fromString(s) of
SOME n => SOME (Int.toString (n * n))|
NONE => NONE;
This has type string -> string option with output now looking like:
- squareString "4";
val it = SOME "16" : string option
- squareString "Bob";
val it = NONE : string option
This would make it the responsibility of the caller to figure out what to do with the option.
The approach to error handling that John explains is elaborated in the StackOverflow question 'Unpacking' the data in an SML DataType without a case statement. The use-case there is a bit different, since it also involves syntax trees, but the same convenience applies for smaller cases:
fun squareString s = Int.fromString s >>= (fn i => SOME (i*i))
Assuming you defined the >>= operator as:
infix 3 >>=
fun NONE >>= _ = NONE
| (SOME a) >>= f = f a
The drawback of using 'a option for error handling is that you have to take into account, every single time you use a function that has this return type, whether it errored. This is not unreasonable. It's like mandatory null-checking. But it comes at the cost of not being able to easily compose your functions (using e.g. the o operator) and a lot of nested case-ofs:
fun inputSqrt s =
case TextIO.inputLine TextIO.stdIn of
NONE => NONE
| SOME s => case Real.fromString s of
NONE => NONE
| SOME x => SOME (Math.sqrt x) handle Domain => NONE
A workaround is that you can build this constant error handling into your function composition operator, as long as all your functions share the same way of expressing errors, e.g. using 'a option:
fun safeSqrt x = SOME (Math.sqrt x) handle Domain => NONE
fun inputSqrt () =
TextIO.inputLine TextIO.stdIn >>=
(fn s => Real.fromString s >>=
(fn x => safeSqrt x))
Or even shorter by applying Eta conversion:
fun inputSqrt () = TextIO.inputLine TextIO.stdIn >>= Real.fromString >>= safeSqrt
This function could fail either because of a lack of input, or because the input didn't convert to a real, or because it was negative. Naturally, this error handling isn't smart enough to say what the error was, so you might want to extend your functions from using an 'a option to using an ('a, 'b) either:
datatype ('a, 'b) either = Left of 'a | Right of 'b
infix 3 >>=
fun (Left msg) >>= _ = Left msg
| (Right a) >>= f = f a
fun try (SOME x) _ = Right x
| try NONE msg = Left msg
fun inputLine () =
try (TextIO.inputLine TextIO.stdIn) "Could not read from stdIn."
fun realFromString s =
try (Real.fromString s) "Could not derive real from string."
fun safeSqrt x =
try (SOME (Math.sqrt x) handle Domain => NONE) "Square root of negative number"
fun inputSqrt () =
inputLine () >>= realFromString >>= safeSqrt
And trying this out:
- ​inputSqrt ();
​9
> val it = Right 3.0 : (string, real) either
- ​inputSqrt ();
​~42
> val it = Left "Square root of negative number" : (string, real) either
- ​inputSqrt ();
Hello
> val it = Left "Could not derive real from string." : (string, real) either
- (TextIO.closeIn TextIO.stdIn; inputSqrt ());
> val it = Left "Could not read from stdIn." : (string, real) either

getting expression doesn't match error

I am trying to implement a node delete function for a Binary Search Tree in SML/nj.
However I am getting a constraint error, that I don't understand why...
datatype 'a tree = Empty | Node of 'a * 'a tree * 'a tree;
datatype 'a stree = STree of ('a * 'a -> bool) * ('a * 'a -> bool) * 'a tree;
fun removeMin Empty = Empty
| removeMin (Node(_,Empty,r)) = r
| removeMin (Node(k,l,r)) = Node(k, removeMin l, r);
removeMin: 'a tree -> 'a tree;
fun get_left_most Empty = Empty
| get_left_most (Node(k,Empty,r)) = Node(k,Empty,r)
| get_left_most (Node(_,l,_)) = get_left_most l;
get_left_most: 'a tree -> 'a tree;
fun get_key (Node(k, l, r)) = k;
get_key: 'a tree -> 'a;
fun tree_empty Empty = true
| tree_empty (Node(_,_,_)) = false;
tree_empty: 'a tree -> bool;
fun remove v (STree(f, g, stree2)) =
let
fun remove2 v Empty = Empty
| remove2 v (Node(k,l,r)) =
if f(v, k) then
if (tree_empty l) then r
else if (tree_empty r) then l
else Node(get_key (get_left_most r), l, removeMin r)
else if g(v, k) then Node(k, (remove2 v l), r)
else Node(k, l, remove2 v r);
in
STree(f, g, (remove2 v stree2))
end;
remove: 'a -> 'a stree -> 'a stree;
This is the error that I am getting: (for get_key)
Warning: match nonexhaustive
Node (k,l,r) => ...
Does anyone know why this is happening?
Your comparison using = in remove means that the tree must contain equality types (hence the two ' characters in the ''Z type variable that SML inferred) but you have claimed that it's more general: remove: 'a -> 'a stree -> 'a stree;.
You need to either only use equality types (i.e. declare remove: ''a -> ''a stree -> ''a stree;)
or redefine remove2 to use case analysis instead of comparison.
For instance,
| remove2 v (node as Node(k, Empty, Empty)) = if f(v, k) then Empty else node

What does 'int ?. tree' mean in an SML error message?

I have the following SML code that I wrote for a class:
fun lookup (cmp: 'a * 'a -> order) (x: 'a, t: 'a tree) : 'a option =
case t of
Empty => NONE
| Node(l,y,r) =>
case cmp(x,y) of
EQUAL => SOME y
| LESS => lookup (cmp) (x,r)
| GREATER => lookup (cmp) (x,l)
In testing this with:
val SOME 3 = lookup Int.compare (3, Node(Empty,3,Empty));
And getting the following error back:
stdIn:153.1-166.12 Error: operator and operand don't agree [tycon mismatch]
operator domain: int * int ?.tree
operand: int * int tree
in expression:
(lookup Int.compare) (3,Node (Empty,3,Empty))
What does the ?. mean?
This is usually to do with restricted visibility across modules. What does your 'tree' definition look like? You might need to tell the compiler that your 'tree' type in one module is the same as the one in another module.

GHC rejects ST monad code as unable to unify type variables?

I wrote the following function:
(.>=.) :: Num a => STRef s a -> a -> Bool
r .>=. x = runST $ do
v <- readSTRef r
return $ v >= x
but when I tried to compile I got the following error:
Could not deduce (s ~ s1)
from the context (Num a)
bound by the type signature for
.>=. :: Num a => STRef s a -> a -> Bool
at test.hs:(27,1)-(29,16)
`s' is a rigid type variable bound by
the type signature for .>=. :: Num a => STRef s a -> a -> Bool
at test.hs:27:1
`s1' is a rigid type variable bound by
a type expected by the context: ST s1 Bool at test.hs:27:12
Expected type: STRef s1 a
Actual type: STRef s a
In the first argument of `readSTRef', namely `r'
In a stmt of a 'do' expression: v <- readSTRef r
Can anyone help?
This is exactly as intended. An STRef is only valid in one run of runST. And you try to put an external STRef into a new run of runST. That is not valid. That would allow arbitrary side-effects in pure code.
So, what you try is impossible to achieve. By design!
You need to stay within the ST context:
(.>=.) :: Ord a => STRef s a -> a -> ST s Bool
r .>=. x = do
v <- readSTRef r
return $ v >= x
(And as hammar points out, to use >= you need the Ord typeclass, which Num doesn't provide.)