dok/spec/nested-transactions-semantic
A Dok action can:
- return a result
- fail because a logical condition is not meet
- raise an exception because there is an exceptional external problem
Failing is considered a normal state of computation and not an error, expecially when the computation involves persistent (i.e. rollbackable) values. The fail is managed using a (predictable) nested transaction semantic (TR):
- all the effects of the actions executed before the fail are rollbacked
- the caller is informed of the fail
- the caller can manage the fail using another code path or it can fail itself
Semantic
False conditions
A code block testing a false condition fails.
var b1 False
check b1 == False
var e1 case {
when b1
#-NOTE this transaction fail, because `b1` return `False`
return "a"
} -else {
return "b"
}
check e1 == "b"
# Different code with same semantic
set e2 if b1 {
return "a"
} -else {
return "b"
}
try
and case
try
tries different branches, rollbacking in case of failure, until there is a branch that succeed. A try
without a succesfull branch is not considered a failure.
case
is similar to try
, but if no branch succeed, then it is considered a failure.
In this example try
will return a result.
var r try {
when 10 > 100
return "never returned"
} else {
when 10 > 50
return "also this is nevere returned"
} else {
when 10 > 5
return "this is the result"
}
assert r.isa(String) && r == "this is the result"
In this example, try
has only side effects.
var i::Int 0
try {
inc i
when i == 1
when 10 > 100
#-NOTE failing condition, so everything is rollbacked
} else {
assert i == 0
#-NOTE effects of previous branch are rollbacked
when 10 > 50
#-NOTE failing condition
} else {
set i 100
}
assert i == 100
}
Loops
Fail info
A failing block of code or function can add more info about the reason of failure. This info can be used for better error-reporting, e.g. in case of user input-validation. Note that on the contrary of exceptions these are logical failures expected in the code, and not exceptional one. They are similar to the the Haskell type Either SomeErrorInfo Result
.
Fail info can be expensive to generate, so it will be effectively generated only if one of the caller is using it. Otherwise the compiler will disable its generation.
data Age -like Int
String.to(Age) {
var r::Int 0
repeat {
with ch::Char -in self
with i from 0
with b::Int -from 1 -next b * 10
var c::Int ch.ord - '0'.ord
inc r b * c
when c < 0 || c > 9 {
fail "Unexpected char '${ch}' at position ${i}
#-NOTE at the end of this code the fail is generated
}
}
when r >= 150 {
fail "Unreasonable age ${r}"
}
return r
}
try {
"10ab".parseAge
} else {
do println("Unable to parse age for reason: ${~fail.last}")
}
TODO Describe rolback of actions
TODO the rollback depends from the system TODO the rolback manager can decide the policy: signal an error; create a potentially expensive repair and rollback operation. TODO some value based code can be annotated and managed as non-rollbackable code, so faster code is produced, and in case of rollback an exception will be raised