An attempt to generalize it so that the type is part of the macro, and the
initial value is another macro.
macdef
ifnerr(x, err, ec, t) =
(
if
(,(err) = ,(t)(0))
then let
val () = ,(err) := ,(x)
in
if ,(err) != ,(t)(0) then ,(err) := ,(t)(,(ec))
end // end of [then]
)
macdef err (z, t) = ,(t)(,(z))
implement i2c_read_byte(address: uint8): (uint8, uint8) = let
var err: uint8 = err (0, u8)
val () = ifnerr(i2c_start(), err, 1, u8)
val () = ifnerr(i2c_write(wa address), err, 2, u8)
val b = i2c_read(u8(WITH_NACK))
val () = i2c_stop()
in (err, b) endOn Wednesday, September 30, 2015 at 7:33:51 AM UTC-6, gmhwxi wrote:
I got it wrong. Here is one more attempt:
//
macdef
ifnerr(x, err, ec) =
(
if
(,(err)=0)
then let
val () = ,(x)
in
if ,(err) != 0 then ,(err) := ,(ec)
end // end of [then]
)
//
(* ****** ****** *)
//
implement
i2c_read_word_data
(addr, command, err) = let
//
val () = err := 0
//
val () = ifnerr(i2c_start(err), err, 1)
val () = ifnerr(i2c_write(wa addr, err), err, 2)
val () = ifnerr(i2c_write(command, err), err, 3)
val () = ifnerr(i2c_start(err), err, 4)
val () = ifnerr(i2c_write(ra addr, err), err, 5)
//
in
u16(0)
end // end of [i2c_read_word_data]
See a running version at:
https://github.com/githwxi/ATS-Postiats-test/blob/master/contrib/hwxi/TEST10/test17.dats
On Wednesday, September 30, 2015 at 9:15:45 AM UTC-4, gmhwxi wrote:
It looks good.
Overall, I feel that the style is a bit too functional.
If a function may generate an error, a common style is to have it carry a
call-by-reference argument:
(* ****** ****** )
//
extern
fun i2c_start(err: &int >> _): void
extern
fun i2c_write(uint8, err: &int >> _): void
//
( ****** ****** )
//
extern
fun
i2c_read_word_data
(uint8, uint8, err: &int >> _) : uint16
//
( ****** ****** *)
//
implement
i2c_read_word_data
(addr, command, err) = let
//
val () = err := 0
//
val () =
if iseqz(err) then i2c_start(err)
val () = if err != 0 then err := 1
//
val () =
if iseqz(err) then i2c_write(wa addr, err)
val () = if err != 0 then err := 2
//
val () =
if iseqz(err) then i2c_write(command, err)
val () = if err != 0 then err := 3
//
val () =
if iseqz(err) then i2c_start(err)
val () = if err != 0 then err := 4
//
val () =
if iseqz(err) then i2c_write(ra addr, err)
val () = if err != 0 then err := 5
//
in
u16(0)
end // end of [i2c_read_word_data]
(* ****** ****** *)
On Wednesday, September 30, 2015 at 12:22:10 AM UTC-4, Mike Jones wrote:
In the spirt of the example link, considering that all functions return
0 or pass or 1 for fail, I came up with this. A little verbose, but easy to
understand because things line up in columns for inspection.
fun wa (addr:uint8) : uint8 = addr << 1
fun ra (addr:uint8) : uint8 = (addr << 1) + u8(1)
implement i2c_read_word_data(address: uint8, command: uint8): (int8,
uint16) = let
var r: int8 = i2c_start()
val () = if r = i8(0) then r := r + i8(2) * i2c_write(wa address)
val () = if r = i8(0) then r := r + i8(3) * i2c_write(command)
val () = if r = i8(0) then r := r + i8(4) * i2c_start()
val () = if r = i8(0) then r := r + i8(5) * i2c_write(ra address)
val d1 = i2c_read(WITH_ACK)
val d0 = i2c_read(WITH_NACK)
val () = i2c_stop()
val d = (u16(d1) << 8) + u16(d0)
in (r, d) end
On Tuesday, September 29, 2015 at 7:46:23 AM UTC-6, gmhwxi wrote:
Error-handling is a challenging issue in general.
In your case, there seems to be no cleanup involved.
Here is something you could do:
https://github.com/githwxi/ATS-Postiats-test/blob/master/contrib/hwxi/TEST10/test16.dats
To me, there is nothing wrong with a sequence of if-then-else’s; it is
just that this style of code
is a bit difficult to write.
One possibility is to use templates. For instance:
//
extern
fun{}
the_get_nerror(): int
//
extern
fun{}
i2c_write_if(…): …
//
implement
{}(tmp)
i2c_write_if(…)
if the_get_nerror() > 0 then i2c_write(…)
// end of [i2c_write_if]
Then you can just write something like
val () = i2c_write_if(…)
where i2c_write_if is guarded by the condition (the_get_nerror() > 0).
You can readily implement the template the_get_nerror locally so as to
suit
your need. This is just a form of late-binding.
On Tuesday, September 29, 2015 at 2:05:39 AM UTC-4, Mike Jones wrote:
Being a newbie, I’m not sure how to handle early exit. In C, code
often checks an error at each line of a sequence, and if there is an error,
cleanup and return. In Haskell perhaps one would use a Maybe monad and
string computations together.
I have recoded a piece of C (below), but could not find a way to
handle early exit. So it is a nested mess of conditionals.
Is there some natural way to express this in ATS2? Exceptions would
help if ATS2 has them. I did not find anything to suggest it has, but
exceptions would have to work without a malloc/gc in my case.
Any suggestions? My only though was a FSA and make it recursive where
it marches from state to state, and a failure goes to a failure state and
exists. But is there some simple way to exit a sequence from any point in
it?
implement i2c_read_word_data(address: uint8, command: uint8): (int8,
uint16) = let
val r = i2c_start()
val r’ = if (r = cast{int8}(0)) then let
val r’’ = i2c_write((address * cast{uint8}(2)) +
cast{uint8}(0))
val r''' = if (r'' = cast{int8}(0)) then let
val r'''' = i2c_write(command)
val r''''' = if (r'''' = cast{int8}(0))
then let
val r’‘’‘’’ = i2c_start()
val r’‘’‘’‘’ = if (r’‘’‘’’
= cast{int8}(0)) then let
val
r’‘’‘’‘’’ = i2c_write((address * cast{uint8}(2)) + cast{uint8}(1))
val
r’‘’‘’‘’‘’ = if (r’‘’‘’‘’’ = cast{int8}(0)) then let
val d1 = i2c_read(cast{int8}(0))
val d0 = i2c_read(cast{int8}(1))
in cast{int8}(0) end
else
cast{int8}(5)
in
r’‘’‘’‘’‘’ end
else
cast{int8}(4)
in r’‘’‘’‘’ end
else
cast{int8}(3)
in r’‘’‘’ end
else
cast{int8}(2)
in r’‘’ end
else
cast{int8}(1)
in (r’, cast{uint16}(0)) end