Early Exit

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

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.datsOn 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

Error-handling is a challenging issue in general.
In your case, there seems to be no cleanup involved.
Here is something you could do:

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

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

,(…) is borrowed from LISP; it is part of the backquote-comma-notation for
write macros.

For the second question, you can do:

extern int funA (): uint8

implement funA() = u8($extfcall(int8, “funA”))

In this case, I think the following code should be enough:

implement funA() = $extfcall(uint8, “funA”)

Interesting, basically, you have similar goals, which is to invent a
language for the user of an interface.

A couple of questions, the common in (,(err))=0 and = ,(x), what does it
mean?

Second, is there a way to alias function names. For example, suppose in C I

One problem with using macros is that error message reporting can get
really BAD.On Wednesday, September 30, 2015 at 6:49:40 PM UTC-4, Mike Jones wrote:

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) end

On 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

The hope is to get your code to look like:

implement
i2c_read_word_data
(
address, command
) = let
//
var r: int8 = i8(0)
var nerror: int = 0
//
implement
the_get_nerror<>() = $UN.ptr0_get(addr@nerror)
//
val () = i2c_start_if()
val () = i2c_write_if((address * u8(2)) + u8(0))
val () = i2c_write_if(command)
val () = i2c_start_if()
val () = i2c_write_if((address * u8(2)) + u8(1))
//
val d0 = i2c_read(i8(0))
val d1 = i2c_read(i8(1))
//
in
(r, u16(0))
end // end of [i2c_read_word_data]On Tuesday, September 29, 2015 at 9:46:23 AM UTC-4, 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

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) endOn 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

True, but I think in this case, the result is reasonably clear code, with
types that have boundaries to their values.

Related to boundaries, with the types like uint8 and int8, what happens if
you add them to values they can’t represent? Do they overflow at 8 bits, or
do they add like an int and truncate? Are there any static techniques to
prevent overflow?

In the code at hand, a constraint that you can add or subtract would work,
or a general constraint that you can’t calculate with the passed in value.

I’m kind of hoping that the lastest version with the macros, which no
longer has multiplication of values, might be restricted to use in function
calls.

My guess is the answer is no, because no values are returned to put a
constraint on, as in a dependent type. But if there is a way to return a
value in the static description that is not in the dynamic description,
perhaps there is a way? But even if so, that does not prevent using
intermediate values in the implementation.

Any thoughts on how this might be done?

Context for the question is a designer designing the function interfaces
that protect against a careless maintainer, assuming a social contract that
keeps them from mucking with interfaces :-)On Wednesday, September 30, 2015 at 5:26:13 PM UTC-6, gmhwxi wrote:

One problem with using macros is that error message reporting can get
really BAD.

On Wednesday, September 30, 2015 at 6:49:40 PM UTC-4, Mike Jones wrote:

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) end

On 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

In principle, you could use dependent types to prevent overflow. E.g,

extern
fun wa2 {i:nat | i <= 127} (addr:uint8(i)): uint8(2i)
extern
fun ra2 {i:nat | i <= 127} (addr:uint8(i)): uint8(2
i+1)

Of course, doing so can make it a lot harder to write well-typed code.On Wednesday, September 30, 2015 at 11:52:17 PM UTC-4, Mike Jones wrote:

True, but I think in this case, the result is reasonably clear code, with
types that have boundaries to their values.

Related to boundaries, with the types like uint8 and int8, what happens if
you add them to values they can’t represent? Do they overflow at 8 bits, or
do they add like an int and truncate? Are there any static techniques to
prevent overflow?

In the code at hand, a constraint that you can add or subtract would work,
or a general constraint that you can’t calculate with the passed in value.

I’m kind of hoping that the lastest version with the macros, which no
longer has multiplication of values, might be restricted to use in function
calls.

My guess is the answer is no, because no values are returned to put a
constraint on, as in a dependent type. But if there is a way to return a
value in the static description that is not in the dynamic description,
perhaps there is a way? But even if so, that does not prevent using
intermediate values in the implementation.

Any thoughts on how this might be done?

Context for the question is a designer designing the function interfaces
that protect against a careless maintainer, assuming a social contract that
keeps them from mucking with interfaces :slight_smile:

On Wednesday, September 30, 2015 at 5:26:13 PM UTC-6, gmhwxi wrote:

One problem with using macros is that error message reporting can get
really BAD.

On Wednesday, September 30, 2015 at 6:49:40 PM UTC-4, Mike Jones wrote:

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) end

On 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

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

Interesting, basically, you have similar goals, which is to invent a
language for the user of an interface.

A couple of questions, the common in (,(err))=0 and = ,(x), what does it
mean?

Second, is there a way to alias function names. For example, suppose in C I
have

int8_t funA()

And in ATS I want

fun funA():uint8

So I want to create a wrapper like:

fun FunA_() = u8(funA())

But, I want the code to call funA, not funA_. The reason is some functions
may have a wrapper, some not, and I don’t want to create some suffix and
wrapper functions that don’t require wrappers.On 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]
)
//