I am new to ML.
I need to define a function taking an conditional expression as argument, the problem is if the expression is invalid like "10 div 0 = 0". How can I handle this?
For example, the function is defined as following: foo exp1 = if (exp1) then ... else..., and exp1 is "10 div 0 = 0", how to handle this division error.
It looks like you want to ask about exception handling mechanism in SML.
The div function in SML basis library raise Div exception when you invoke 10 div 0. It depends on whether you need the value or not to handle the exception. You can either return true/false or option type in this case:
(* only catch exception, ignore value *)
fun div_check (x, y) = (
ignore (x div y);
false
) handle Div => true
(* catch exception and return option value *)
fun div_check2 (x, y) = (
SOME (x div y)
) handle Div => NONE
UPDATE:
It is really weird that the compiler doesn't raise Div exception in this case. I suggest that you define a custom div function and raise/handle exceptions yourself:
exception DivByZero;
(* custom div function: raise DivByZero if y is zero *)
infix my_div;
fun x my_div y =
if y=0 then raise DivByZero else x div y
fun div_check (x, y) = (
ignore (x my_div y);
false
) handle DivByZero => true
fun div_check2 (x, y) = (
SOME (x my_div y)
) handle DivByZero => NONE
Related
In Java, it is possible to do ->
someList.stream().map(x -> {
y = doSomeOperation(x);
z = doSomeOtherOperation(y);
return z;
}).collect(Collectors.toList());
I need to convert above code to Kotlin. But in all the online tutorials, I am learning that it is only possible to have simple lambdas, like x -> x*x or x->doSomethingThenReturnValue(x).
Is it not possible to write a complex lambda (which does some complex inline operation) like above in kotlin? I tried writing ->
someList.map{ x -> {
y = doSomeOperation(x);
z = doSomeOtherOperation(y);
return z;
}}
But it threw error. Can you please tell what would be the correct way to do it?
I think the problem here is return z. When placed inside lambda it returns from the enclosing function, unlike in Java where it only returns from the lambda itself. So you should write either
someList.map { x ->
y = doSomeOperation(x)
z = doSomeOtherOperation(y)
z
}
or
someList.map { x ->
y = doSomeOperation(x)
z = doSomeOtherOperation(y)
return#map z
}
More details on "return" issue can be found here - https://kotlinlang.org/docs/reference/returns.html
You have written too many brackets. In kotlin, parameter definition for lambdas are set inside the bracket (see reference documentation).
EDIT: Also, return statement in lambdas is not always allowed, and when it is, its behavior is really specific. More information in official documentation.
So, your example needs to be rewritten as following:
someList.map { x ->
val y = doSomeOperation(x)
val z = doSomeOtherOperation(y)
// z implicitely returned as lambda result
z
}
I'm new to Standard ML and trying to write the following code
fun whilestat test stmt1 =
(fn x => if (test x) then (stmt1 x;whilestat test stmt1 ) else (x) );
The issue is that it gives me the following error
w.sml:21.6-22.82 Error: right-hand-side of clause doesn't agree with function result type [circularity]
expression: ('Z -> 'Y) -> 'Z -> 'Z
result type: ('Z -> 'Y) -> 'Z
in declaration:
whilestat2 = (fn arg => (fn <pat> => <exp>))
uncaught exception Error
raised at: ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
../compiler/TopLevel/interact/evalloop.sml:44.55
../compiler/TopLevel/interact/evalloop.sml:292.17-292.20
Im just trying to emaulate a while condition where if the staement is true then it recurses, else returns the value.
The issue lies in the return type of whilestat. In the then branch, you return a function, whereas in the else branch, you return return an arbitrary piece of data. I think you simply forgot to pass all of the arguments when you recurse in the then branch.
Here's how I would write it (notice also that there's no need to use fn x => ..., which I think contributed to your confusion).
fun whilestat test stmt1 x =
if test x
then (stmt1 x; whilestat test stmt1 x)
else x
In the future, you might find it helpful to explicitly annotate types in your source code, to double check your reasoning. I found your bug by trying to fill in the ??? below:
fun whilestat (test : 'a -> bool) (stmt1 : 'a -> unit) : ??? =
...
I'm proving certain properties on elliptic curves and for that I rely on some functions that deal with field operations. However, I don't want Inox to reason about the implementation of these functions but to just assume certain properties on them.
Say for instance I'm proving that the addition of points p1 = (x1,y1) and p2 = (x2,y2) is commutative. For implementing the addition of points I need a function that implements addition over its components (i.e. the elements of a field).
The addition will have the following shape:
val addFunction = mkFunDef(addID)() { case Seq() =>
val args: Seq[ValDef] = Seq("f1" :: F, "f2" :: F)
val retType: Type = F
val body: Seq[Variable] => Expr = { case Seq(f1,f2) =>
//do the addition for this field
}
(args, retType, body)
}
For this function I can state properties such as:
val addAssociative: Expr = forall("x" :: F, "y" :: F, "z" :: F){ case (x, y, z) =>
(x ^+ (y ^+ z)) === ((x ^+ y) ^+ z)
}
where ^+ is just the infix operator corresponding to add as presented in this other question.
What is a proper expression to insert in the body so that Inox does not assume anything on it while unrolling?
There are two ways you can go about this:
Use a choose statement in the body of addFunction:
val body: Seq[Variable] => Expr = {
choose("r" :: F)(_ => E(true))
}
During unrolling, Inox will simply replace the choose with a fresh
variables and assume the specified predicate (in this case true) on
this variable.
Use a first-class function. Instead of using add as a named function,
use a function-typed variables:
val add: Expr = Variable(FreshIdentifier("add"), (F, F) =>: F)
You can then specify your associativity property on add and prove the
relevant theorems.
In your case, it's probably better to go with the second option. The issue with proving things about an addFunction with a choose body is that you can't substitute add with some other function in the theorems you've shown about it. However, since the second option only shows things about a free variable, you can then instantiate your theorems with concrete function implementations.
Your theorem would then look something like:
val thm = forallI("add" :: ((F,F) =>: F)) { add =>
implI(isAssociative(add)) { isAssoc => someProperty }
}
and you can instantiate it through
val isAssocAdd: Theorem = ... /* prove associativity of concreteAdd */
val somePropertyForAdd = implE(
forallE(thm)(\("x" :: F, "y" :: F)((x,y) => E(concreteAdd)()(x, y))),
isAssocAdd
)
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
Let's have a function foo and a class Bar:
fun foo(key: String): String? {
// returns string or null
}
class Bar(x: String, y: String) {
// ...
}
Now, let's have the code:
val x = foo("x")
val y = foo("y")
if (x.isNotEmpty() && y.isNotEmpty())
return Bar(x, y)
The problem is that this code will not compile. Since it needs the Bar(x!!, y!!).
However when I replace the function with its content, !! are not needed.
val x = foo("x")
val y = foo("y")
if ((x != null && x.length() > 0) && (y != null && y.length() > 0))
return Bar(x, y)
Why it is not possible to resolve the null check from the function .isNotEmpty()?
This is possible in theory, but it would mean that either
1. The declaration of isNotEmpty() must convey to the compiler the fact that x is guaranteed to be non-null if the result is true
2. A change to a body of any function may cause its call sites to fail to compile.
Option 2 is definitely unacceptable. Option 1 requires a rather expressive mechanism in the type system, which we decided not to add at the moment, because it is likely to complicate things for the user.
We are planning to support something like this with inline functions, but it's still under consideration.