One way to implement "early returns" in OCaml is via exceptions:
exception Exit
let myfunc () =
try
for i = 0 to .... do
if .. then raise Exit
done; false
with Exit -> true
However, is there a way to declare this Exit exception in the body of the function, so its name is not visible to other functions in the module?
(* I would like to do this, but it gives a syntax error *)
let myfunc () =
exception Exit
try
for i = 0 to .... do
if .. then raise Exit
done; false
with Exit -> true
Yes, what you want is possible by using a local module:
let myfunc () =
let module M = struct exception Exit end in
try
for i = 0 to 3 do
if true then raise M.Exit
done; false
with M.Exit -> true
That style is not particularly pleasant to read, though, so I wouldn't recommend it. It is enough to omit showing Exit at the next module interface if you want to hide it from most of the rest of the program.
Related
I am struggling to use an if/else on a containsAll() statement. It returns the correct true false value when tested with println(), but when put in an if statement it seems to always evaluate to true -- see below.
def examine_phenotype(pheno){
condition_values = \
Channel
.fromPath(pheno)
.splitCsv(header: true, sep: ',')
.map{ row ->
def condition = row.condition
return condition
}
.toList().view()
println(condition_values.containsAll('control'))
if(condition_values.containsAll('control')){
exit 1, "eval true"
}else{
exit 1, "eval false"
}
}
Console output for two different files, one with 'control' and one without 'control' in the column 'condition', which is the point of the function.
[normal, normal, normal, tumor, tumor, tumor]
DataflowInvocationExpression(value=false)
eval true
[control, control, control, tumor, tumor, tumor]
DataflowInvocationExpression(value=true)
eval true
Using collect() instead of toList() where each item within condition_values is enclosed with single quotes did not resolve the issue either. The clue might be in DataflowInvocationExpression but I am not up to speed on Groovy yet and am not sure how to proceed.
Testing the conditional within the function was not working, but applying filter{} and ifEmpty{} was able to produce the desired check:
ch_phenotype = Channel.empty()
if(pheno_path){
pheno_file = file(pheno_path)
ch_phenotype = examine_phenotype(pheno_file)
ch_phenotype.filter{ it =~/control/ }
.ifEmpty{ exit 1, "no control values in condition column"}
}
def examine_phenotype(pheno){
Channel
.fromPath(pheno)
.splitCsv(header: true, sep: ',')
.map{ row ->
def condition = row.condition
return condition
}
.toList()
}
I am using Lens with Amazonka to deal with errors but I struggle to deal with errors:
doSignup e p = (AWS.send $ AWS.signUp "secret" e p)
$> Right ()
& catching_ AWS._UsernameExistsException (return $ Left AlreadyExistingEmail)
& catching_ AWS._InvalidPasswordException (return $ Left WeakPassword)
& catching AWS._SertviceError (return . UnknownSignUpError)
data SignUpError where
AlreadyExistingEmail :: SignUpError
NotAnEmail :: SignUpError
WeakPassword :: SignUpError
UnknownSignUpError :: forall a. Show a => a -> SignUpError
I struggle to have a consistent catching behavior, when a _UsernameExistsException is thrown I got a Left WeakPassword.
It becomes weirder because it works when I drop the WeakPassword line.
While I get the right error (keeping only the last line):
expected: Right ()
but got: Left UnknownSignUpError ServiceError' {_serviceAbbrev = Abbrev "CognitoIdentityProvider", _serviceStatus = Status {statusCode = 400, statusMessage = "Bad Request"}, _serviceHeaders = [("Date","Tue, 06 Oct 2020 05:38:56 GMT"),("Content-Type","application/x-amz-json-1.1"),("Content-Length","96"),("Connection","keep-alive"),("x-amzn-RequestId","b09210a3-41ed-46ee-af4f-46db58b98695"),("x-amzn-ErrorType","UsernameExistsException:"),("x-amzn-ErrorMessage","An account with the given email already exists.")], _serviceCode = ErrorCode "UsernameExists", _serviceMessage = Just (ErrorMessage "An account with the given email already exists."), _serviceRequestId = Just (RequestId "b09210a3-41ed-46ee-af4f-46db58b98695")}
I have tried to use catches but handler requires Typeable Lenses which is not the case.
How can I have a "pattern matching-like" way to deal with exceptions? Thanks in advance.
In fact Control.Lens.catching was used instead of Network.AWS.Prelude.catching which was messing up the exception handling.
I have programmed a langton's ant and it work nice.
Now I want to run 2 ant simultaneously.I have a run function how make the computation and ant's movement and it's a infinite run loop.
How can I run 2 of this loop at once ?
I have try to look on Thread but i'm not sure that it's the best for my case.
This is some example of my code :
Run function :
let run(f,tab2 : t_fourmi*int array array) =
f.xx := !(f.x);
f.yy := !(f.y);
let d = ref 0
and z = ref 0
and o = ref 0
(* 1 = de bas, 2 = de droite, 3 = de haut, 4 = de gauche *)
in
if tab2.(!(f.x)/5).(!(f.y)/5) = 0
then move_right(f,1,tab2);
if !(f.xx) + 5 = !(f.x)
then d := 4
else if !(f.xx) - 5 = !(f.x)
then d := 2
else if !(f.yy) + 5 = !(f.y)
then d := 1
else if !(f.yy) - 5 = !(f.y)
then d := 3;
while true
do
(*
print_string "step : ";
print_int !o;
print_newline(); *)
o := !o + 1;
f.xx := !(f.x);
f.yy := !(f.y);
z := tab2.(!(f.x)/5).(!(f.y)/5);
if !z = 0
then move_right(f,!d,tab2)
else if !z = 1
then move_left(f,!d,tab2)
else if !z = 2
then move_right(f,!d,tab2)
else if !z = 3
then move_right(f,!d,tab2);
if !(f.xx) + 5 = !(f.x)
then d := 4
else if !(f.xx) - 5 = !(f.x)
then d := 2
else if !(f.yy) + 5 = !(f.y)
then d := 1
else if !(f.yy) - 5 = !(f.y)
then d := 3;
done;
;;
Example of move function :
let move_left(f,d,tab2 : t_fourmi*int*int array array) = (* d = direction d'ou la fourmi viens *)
(* 1 = de bas, 2 = de droite, 3 = de haut, 4 = de gauche *)
f.xx := !(f.x);
f.yy := !(f.y);
if d = 1
then f.x := !(f.x) - 5
else if d = 2
then f.y := !(f.y) - 5
else if d = 3
then f.x := !(f.x) + 5
else if d = 4
then f.y := !(f.y) + 5;
if !(f.x) >= 995
then f.x := 5
else if !(f.x) <= 5
then f.x := 995;
if !(f.y) >= 995
then f.y := 5
else if !(f.y) <= 5
then f.y := 995;
create_fourmi(f);
let n = tab2.(!(f.xx)/5).(!(f.yy)/5) in
drawinv(!(f.xx),!(f.yy),n,tab2);
;;
If you need more function, ask me.
Thanks
If I understand your code correctly (it's unfortunately not very readable), it could be outlined as essentially:
let init params =
...
let step state =
...
let move thing state =
...
let run params thing =
let state = ref (init params) in
while true do
let state := step state in
move thing state
done
where I've factored init, step and move out into separate function, which should be pretty straight-forward to do. And by doing so we can replace the run function with a run_two function that can run two instances virtually at the same time, though not entirely in parallel. They won't run independently, but in synchrony, iteration by iteration:
let run_two params1 thing1 params2 thing2 =
let state1 = ref (init params1) in
let state2 = ref (init params2) in
while true do
state1 := step !state1;
state2 := step !state2;
move !state1;
move !state2;
done
This doesn't use any threads or other complicated concurrency primitives. It's just ordinary code organized in a way that allows composition. It also allows the init and step function to be completely pure, which makes them easy to test and reason about. You could even make the move functions pure, if you factor out the drawing.
You can use threads, though it will open a pandora box for you. You have to synchronize your ants, since probably you want them to move in the same world.
Running two ants in different threads
First of all, you have to represent each ant process as an ever-looping function of type ant -> unit, where ant is the type that describes ant's initial position, moving parameters, and so on (if you don't need this, then just use unit instead. So suppose we have a function val run : ant -> unit. Next, we need to make sure, that we do not write to the same board at the same time from different threads, so we need to create a mutex, using Mutex.create, we then need to update our run function and do Mutex.lock before updating our board, and Mutex.unlock after. Finally, we should ensure that no ants will starve for the board, and that once an ant finishes it moves it yields the control to another ant, we could do this using Thread.delay if we want our simulation to be smooth (i.e., if we want to artificially slow it down to human-observable speed). Otherwise, we can just use Thread.yield. (Note, normally threads are preempted (forced to yield) by the system, but this is done in special yield points. When we do system programming there are usually lots of yield points, but this is not our case, so we have to implement this cooperation explicitly). Finally, our updated run function is ready to be run in a thread, with Thread.createrun ant. This function will return to our main thread of execution, so we can run a second ant and so on. Once all ants start running we have to call Thread.join to wait for all of them. Ideally, this function should never return.
I hope, that the above outline provided enough information for you, and you can enjoy the coding yourself. Feel free to ask questions int the comment section if anything is unclear. Probably, the first question would be how to compile it. It is (there are simpler ways, of course, but the most basic is, assuming that your program is in the ant.ml):
ocamlopt -thread unix.cmxa threads.cmxa ant.ml -o ant
Running two ants in co-routines
While the above will work, it seems too complicated and too system-dependent for such a simple simulation task. Why do we need to use system threads for that? We actually don't, we can be clever and implement co-routines using plain OCaml and continuation-passing style. Don't worry, a continuation is just a function. And a continuation-passing style is when we are calling another function at the end of the other function. In this approach, we will not have the run function that runs infinitely, instead, we will have a step function that advances an ant one step forward at each invocation. For the demonstration purposes, let's simplify our step function so that each ant just greets another, e.g.,
let step other yield =
print_endline ("Hello, " ^ other);
yield ()
That simple. We do our step, and then call the yield function (that is that fancy continuation) to yield the control to the next ant. Now let's tie it together in a simple infinite loop, e.g.,
let rec loop () =
step "Joe" ## fun () ->
step "Doe" ## fun () ->
loop ()
let () = loop ()
Let's make it clear. step "Joe" ## fun () -> <continue> is the same as step "Joe" (fun () -> <continue>), and this (fun () -> <continue>) is the yield function that is passed to the step function that greets Joe. Once Joe is greeted the step function calls us again and we evaluate the <continue>, which is, in our case step "Doe" ## fun () -> loop (), i.e., we pass the fun () -> loop () function as the yield argument to the step function that greets Doe, so once Doe is greeted, we call loop () ... and now we're at the begining of the loop.
In our case, we were passing a value of type unit in our continuations, but we can also pass an arbitrary value, e.g., a value that will represent the state of our simulation (the board with ant positions). This will enable a functional implementation of your simulation if you want to try it.
I'm playing around with the Julia testsuite functionality and I quite like it.
What i however fail to figure out is how to make the testsuite report the potential message upon completion.
Let's say I have a test suite like the one below, where each of the functions performs a bunch of tests in turn before returning
#testset "MyTestSuite" begin
#testset "Subtest1" begin #test my_test_1() end
#testset "Subtest2" begin #test my_test_2() end
#testset "Subtest3" begin #test my_test_3() end
#testset "Subtest4" begin #test my_test_4() end
end
If now say my_test_4 fails or throws an error, then the ouput would look like below
Test Summary: | Pass Error Total
MyTestSuite | 65 1 66
Subtest1 | 5 1 6
Subtest2 | 10 0 10
Subtest3 | 20 0 20
Subtest4 | 30 0 30
ERROR: LoadError: Some tests did not pass: 65 passed, 0 failed, 1 errored, 0 broken.
But there is now way (to my knowledge) for me to see what went wrong without scrolling upwards in the terminal output. If my test suites are long enough, and produce enough diagnostics, the error information could even be lost to me or at least very difficult to find.
So, does anyone know a nice way around this? Are there options that one can give to the macro #testset to make sure it prints of collects the errors for further processing?
You can define your custom AbstractTestSet. It is descirbed here in the Julia manual.
Here is an example adapted from the manual. First define:
using Test
struct CustomTestSet <: Test.AbstractTestSet
description::AbstractString
results::Vector
CustomTestSet(desc) = new(desc, [])
end
Test.record(ts::CustomTestSet, child::Test.AbstractTestSet) = push!(ts.results, child)
Test.record(ts::CustomTestSet, res::Test.Result) = push!(ts.results, res)
function Test.finish(ts::CustomTestSet)
if Test.get_testset_depth() > 0
Test.record(Test.get_testset(), ts)
end
ts
end
and now you can write:
julia> res = #testset CustomTestSet "custom testset" begin
# this testset should inherit the type, but not the argument.
#testset "custom testset inner" begin
#test 1==1
#test 1==2
#test 2==2
#test 2==3
end
end
CustomTestSet("custom testset", Any[CustomTestSet("custom testset inner", Any[Test Passed, Test Failed at REPL[10]:5
Expression: 1 == 2
Evaluated: 1 == 2, Test Passed, Test Failed at REPL[10]:7
Expression: 2 == 3
Evaluated: 2 == 3])])
julia> res.results[1].results
4-element Array{Any,1}:
Test Passed
Test Failed at REPL[10]:5
Expression: 1 == 2
Evaluated: 1 == 2
Test Passed
Test Failed at REPL[10]:7
Expression: 2 == 3
Evaluated: 2 == 3
and you have an access to a vector that tells you what passed and what failed, and on failure what was the problem.
You can also filter out passed tests:
julia> filter(x -> !(x isa Test.Pass), res.results[1].results)
2-element Array{Any,1}:
Test Failed at REPL[6]:5
Expression: 1 == 2
Evaluated: 1 == 2
Test Failed at REPL[6]:7
Expression: 2 == 3
Evaluated: 2 == 3
If your tests have a more complex nested structure you should do it recursively.
Is this something you wanted?
If the input argument are not as expected I want to exit the program. How should I achieve that? Below is my attempt.
let () =
if ((Array.length Sys.argv) - 1) <> 2 then
exit 0 ; ()
else
()
Thanks.
exit n is a right way to exit program, but your code has a syntax error. if ... then exit 0; () is parsed as (if ... then exit 0); (). Therefore you got a syntax error around else, since it is not correctly paired with then.
You should write:
let () =
if ((Array.length Sys.argv) - 1) <> 2 then begin
exit 0 ; ()
end else
()
or simply,
let () = if Array.length Sys.argv - 1 <> 2 then exit 0