Consistency in the realtime clock + calc example

21 views
Skip to first unread message

Daniel Kersten

unread,
Jul 25, 2010, 10:48:09 PM7/25/10
to anic
Hi,

I'm trying to refamiliarize myself with ANI after some time away from it and have come across a question.

In the realtime clock + calculator example, won't the clock part use stale data while it is reading input? If not, why not?

Let me explain:

If you type 1 and + then a is assigned 1 and op is assigned '+'. b has not yet been assigned (maybe I'm a slow typist). Now the next clock "tick" kicks in and it prints something like "1 + 0 = 0" (a=1, op=+, b=0, r=0). This is, obviously, incorrect. What we would really like is that the new values of a and op are not seen until b and r are updated too (an atomic transaction, of sorts).


Thanks.
--
Daniel Kersten.
Leveraging dynamic paradigms since the synergies of 1985.

Mark Henderson

unread,
Jul 25, 2010, 11:38:43 PM7/25/10
to anic
Hmm. Is a CR required from the user before std.in sees the new a/b/
op?

Daniel Kersten

unread,
Jul 26, 2010, 1:09:12 AM7/26/10
to mark...@gmail.com, anic
Or just as importantly.. if that is the case (CR is needed to flush the input) then that means that std.in is "pipe aware" - if not, then I can still type 1 CR + CR 2 CR 3 CR - CR 2 CR (or whatever) and see inconsistent results. Similarly I could type more than three symbols before I type a CR. If std.in is aware of how many times it appears in the pipe, then it knows how many tokens belong to a "transaction"... if this is the case, then can we write code like this ourselves?

Not saying anything is wrong with the naive approach (where typing a CR after each token causes inconsistency - that is, the "transaction" is up to the user, not the code). Its a perfectly valid approach. Just asking if this is what ANI does and pointing out possible pitfalls.

On 26 July 2010 06:04, Daniel Kersten <dker...@gmail.com> wrote:
You mean that if I have multiple std.in's non of them fire until the input is flushed by a CR, and then all of them fire? That does make sense alright.

But it still leaves me with another set of questions: how can we handle consistency/synchronisation in the general case? How would we go about building our own queue until flushed mechanisms with ANI code? Or do we not need to? If not, why not?

Adrian

unread,
Jul 26, 2010, 1:32:44 AM7/26/10
to anic
On Jul 25, 7:48 pm, Daniel Kersten <dkers...@gmail.com> wrote:
> Hi,
>
> I'm trying to refamiliarize myself with ANI after some time away from it and
> have come across a question.
>
> In the realtime clock + calculator example, won't the clock part use stale
> data while it is reading input? If not, why not?
>
> Let me explain:
>
> If you type 1 and + then a is assigned 1 and op is assigned '+'. b has not
> yet been assigned (maybe I'm a slow typist). Now the next clock "tick" kicks
> in and it prints something like "1 + 0 = 0" (a=1, op=+, b=0, r=0). This is,
> obviously, incorrect. What we would really like is that the new values of a
> and op are not seen until b and r are updated too (an atomic transaction, of
> sorts).
>
> Thanks.

Hey Dan,

Haven't talked in a while, have we? I guess we've both been busy with
life (don't worry though, anic's not going anywhere!).

Anyway, you are correct in noting that "stale" (non-atomic) reads are
possible with the implementation that's currently up; hence, the
output might be inconsistent at certain points of the input fetching
process. It's not meant to be a robust, clean, and fault-tolerant
program -- it's meant to be a bare-bones implementation in four lines
of code ;).

We could easily encapsulate the computation/output into a "calculator"
object with a "string outstructor" that gives an atomic string
representation of the computation -- it would however defeat the point
of the sample program, which is meant to be simple and tiny. I'm sure
you're waiting for an example, so here it is. I hope it's insightful.

calculator = {
=; // empty instructor; simply means a calculator object can be
instantiated from "null"

// interface
leftOperand = [int\];
rightOperand = [int\];
operator = [char\];

resultBuffer = ""; // initially blank

// core computation logic
{
tempResult = [string\];
\operator ?? {
'+': (\leftOperand+\rightOperand)
'-': (\leftOperand-\rightOperand)
'*': (\leftOperand*\rightOperand)
'/': (\leftOperand/\rightOperand)
: 0
} ->tempResult;
(leftOperand + [string](operator) + rightOperand + '=' + tempResult)
<->resultBuffer ==>; // note that ==> is the loopback operator, which
is basically an inline invocation of the enclosing filter block
};

=[-->string] {resultBuffer -->}; // string outstructor (allows a
calculator object to be used in the context of a string by allowing
the implicit conversion)
}

The above calculator object updates its internal resultBuffer only
upon full computation of a result, and hence its output (string
representation) is always going to be consistent. ANI/anic can't guess
which data the programmer intends to be atomic (and what not), since
in interactive, real-time applications, atomicity is a highly context-
dependent implementation decision. ANI does, however (I think) make it
easy to build systems that behave in whatever way the programmer
intends, which is the main purpose of a programming language in the
first place.

Hopefully this helps!

Adrian

unread,
Jul 26, 2010, 1:36:13 AM7/26/10
to anic
On Jul 25, 8:38 pm, Mark Henderson <markh...@gmail.com> wrote:
> Hmm.  Is a CR required from the user before std.in sees the new a/b/
> op?

No, I wasn't intending that to be the default behavior (I was instead
intending the injection into std.inBlah to be instantaneous) -- I
intend the behavior to be more like a direct link to the keyboard than
a shell-mediated prompt.

Daniel Kersten

unread,
Jul 26, 2010, 1:39:43 AM7/26/10
to ult...@gmail.com, anic
On 26 July 2010 06:36, Adrian <ult...@gmail.com> wrote:
On Jul 25, 8:38 pm, Mark Henderson <markh...@gmail.com> wrote:
> Hmm.  Is a CR required from the user before std.in sees the new a/b/
> op?

No, I wasn't intending that to be the default behavior (I was instead
intending the injection into std.inBlah to be instantaneous) -- I
intend the behavior to be more like a direct link to the keyboard than
a shell-mediated prompt.

This was the assumption I had made too, which is where the question came from. I think this is the better way anyway, since it means that buffered input can be coded in ANI on top of an unbuffered builtin std.in.
 

>
> On Jul 25, 9:48 pm, Daniel Kersten <dkers...@gmail.com> wrote:
>
>
>
> > Hi,
>
> > I'm trying to refamiliarize myself with ANI after some time away from it and
> > have come across a question.
>
> > In the realtime clock + calculator example, won't the clock part use stale
> > data while it is reading input? If not, why not?
>
> > Let me explain:
>
> > If you type 1 and + then a is assigned 1 and op is assigned '+'. b has not
> > yet been assigned (maybe I'm a slow typist). Now the next clock "tick" kicks
> > in and it prints something like "1 + 0 = 0" (a=1, op=+, b=0, r=0). This is,
> > obviously, incorrect. What we would really like is that the new values of a
> > and op are not seen until b and r are updated too (an atomic transaction, of
> > sorts).
>
> > Thanks.
> > --
> > Daniel Kersten.
> > Leveraging dynamic paradigms since the synergies of 1985.

Adrian

unread,
Jul 26, 2010, 2:01:12 AM7/26/10
to anic
On Jul 25, 10:39 pm, Daniel Kersten <dkers...@gmail.com> wrote:
> On 26 July 2010 06:36, Adrian <ulti...@gmail.com> wrote:
>
> > On Jul 25, 8:38 pm, Mark Henderson <markh...@gmail.com> wrote:
> > > Hmm.  Is a CR required from the user before std.in sees the new a/b/
> > > op?
>
> > No, I wasn't intending that to be the default behavior (I was instead
> > intending the injection into std.inBlah to be instantaneous) -- I
> > intend the behavior to be more like a direct link to the keyboard than
> > a shell-mediated prompt.
>
> This was the assumption I had made too, which is where the question came
> from. I think this is the better way anyway, since it means that buffered
> input can be coded in ANI on top of an unbuffered builtin std.in.

My thoughts exactly. By not forcing buffering, that leaves the
flexibility of both unbuffered input and a generic buffer wrapper
overtop of that if it's desired. ANI also supports flexible Google Go-
type interfaces now, so it would be easy to design a generic "buffer"
object that takes in any object that meets some well-defined interface
(such as a "write" oustructor).

This actually all plays in to a bug question that you asked me via
email, so I hope you don't mind that I reproduce it here to clarify
things for everyone:

> "First, a few bugs (which I imagine you're aware of):
>
> 1. std.in cannot be imported, since it doesn't exist, and therefore the symbol is unrecognised. I don't know if you can simply make it "pretend" it exists for now so that the clock+calc code will "compile".
>
> 2. => does not parse."

I actually ended up adding a user-overloadable implicit type cast
system to anic through objects having "instructors" (constructors) and
"outstructors" (type converters) that define implicit data
transformations. Unfortunately, this breaks the original idea of
having std.in infer the exact type to inject into the stream from what
follows it in the pipe (because now what follows can be an object that
accepts multiple different types in its instructors, leaving std.in to
have to guess to disambiguate, which is a huge mess). So instead,
there's now identifiers std.inInt, std.inFloat, etc. that explicitly
specify the type of input that you are would like to parse from the
incoming stream. It's more verbose, but it makes the code easier to
understand (as well as making the language more powerful through its
smart implicit data conversions).

Also, => does not parse as before since => has been eliminated and
replaced by a loopback operator instead of a special type of
declaration. Where you once had

loop => {blah blah loop};

, you would now use

loop = {blah blah <-};

This looks cleaner, it's less redundant by not making you type 'loop'
twice, and tidies up the logic of understanding the previously special
=> type of declaration. You also no longer need to give names to
filters that you'd like to loopback, since loopback operators (<-) can
be inlined anywhere into any filter (even anonymous ones).

***Note*** I screwed this up in the example I gave above (I haven't
looked at anic source in a few weeks, and this is a very recent
addition); ==> doesn't exist, the correct token is <-

Daniel Kersten

unread,
Jul 26, 2010, 2:24:01 AM7/26/10
to ult...@gmail.com, anic

Quick clarification: <- is still also used for initialisation, right?

Besides that, this sounds good! You will need to give us some kind of documentation to learn from (for the interfaces). Sounds like a worthy addition to the language though and answers a lot of the questions about code reuse and writing real world software in ANI that were hiding in the back of my head.
 

Adrian

unread,
Jul 26, 2010, 2:41:36 AM7/26/10
to anic
On Jul 25, 11:24 pm, Daniel Kersten <dkers...@gmail.com> wrote:
Heheh, nope! Sorry, I forgot to mention that, but since the syntax for
loopback and initialization conflicted, I had to rework the
initializer syntax -- but this was a change that I wanted to make
anyway, since I was becoming more and more annoyed at having to always
type a messy <- () whenever I wanted to simply initialize anything.
The new syntax requires any non-empty latch initialization to be
postfixed with a bracketed expression that contains the initializer,
though the brackets can be empty if we are initializing via a null
constructor. For example:

[int](5); // integer latch initialized to 5; the \ isn't necessary
anymore -- it was redundant before anyway
[int](); // integer latch initialized via int's null constructor,
which results in an integer latch containing the value 0

There is also simple syntax for declaring arrays of latches now:

[int[4]](2); // array of 4 int latches, all initialized to contain the
integer 2
[int]{1,2,3}; // array of 3 latches, initialized to contain the
integers in the given list

There are more such minor changes to the language that have been put
in place recently. I just need to get around to documenting them :/.

Daniel Kersten

unread,
Jul 26, 2010, 2:59:45 AM7/26/10
to ult...@gmail.com, anic
So basically all assumptions about syntax are potentially no longer valid? hah.

So no more constant latches? It is nice to see the syntax getting cleaner. Not requiring the \ in declarations is a good change.

Adrian

unread,
Jul 26, 2010, 3:14:45 AM7/26/10
to anic
On Jul 25, 11:59 pm, Daniel Kersten <dkers...@gmail.com> wrote:
> So basically all assumptions about syntax are potentially no longer valid?
> hah.

Worry not; most things have been left untouched. As a matter of fact,
I'm almost sure what we've covered is an exhaustive list :).

>
> So no more constant latches? It is nice to see the syntax getting cleaner.
> Not requiring the \ in declarations is a good change.

There were never "constant latches", I don't think. However, there
were (and still are) constant references to latches (which occur
whenever you refer to a latch without delatching it), though you can't
declare an object itself to be constant anymore. Eventually, though,
I'm planning on implementing contantantization (as well as
encapsulation) rules in the same way that Google Go does it -- namely,
the namingConvention THAT YouUse for your identifiers determines their
encapsulation/constantization properties. Since pretty much everyone
adheres to such ad-hoc conventions anyway, their intuitive nature
might as well be leveraged by the language.

Daniel Kersten

unread,
Jul 26, 2010, 3:51:02 AM7/26/10
to ult...@gmail.com, anic
"constant latches" are mentioned in the tutorial.

Ultimus Freelance

unread,
Jul 26, 2010, 3:59:11 AM7/26/10
to Daniel Kersten, anic

That's my goof, then...

Maybe I wrote it before I established the terminology in my head, but constants and latches are almost opposites as far as the language goes, so that part definitely needs a rewrite. Then again, so does most of the tutorial, with all of the new features and syntax tweaks that have arrived as of late.

On Jul 26, 2010 12:51 AM, "Daniel Kersten" <dker...@gmail.com> wrote:

"constant latches" are mentioned in the tutorial.



On 26 July 2010 08:14, Adrian <ult...@gmail.com> wrote:
>

> On Jul 25, 11:59 pm, Daniel Kersten <...

Daniel Kersten

unread,
Jul 26, 2010, 1:47:08 AM7/26/10
to ult...@gmail.com, anic
On 26 July 2010 06:32, Adrian <ult...@gmail.com> wrote:
On Jul 25, 7:48 pm, Daniel Kersten <dkers...@gmail.com> wrote:
> Hi,
>
> I'm trying to refamiliarize myself with ANI after some time away from it and
> have come across a question.
>
> In the realtime clock + calculator example, won't the clock part use stale
> data while it is reading input? If not, why not?
>
> Let me explain:
>
> If you type 1 and + then a is assigned 1 and op is assigned '+'. b has not
> yet been assigned (maybe I'm a slow typist). Now the next clock "tick" kicks
> in and it prints something like "1 + 0 = 0" (a=1, op=+, b=0, r=0). This is,
> obviously, incorrect. What we would really like is that the new values of a
> and op are not seen until b and r are updated too (an atomic transaction, of
> sorts).
>
> Thanks.

Hey Dan,

Haven't talked in a while, have we? I guess we've both been busy with
life (don't worry though, anic's not going anywhere!).

Indeed. On that point, did you receive my off-list email asking about code gen?
 

Anyway, you are correct in noting that "stale" (non-atomic) reads are
possible with the implementation that's currently up; hence, the
output might be inconsistent at certain points of the input fetching
process. It's not meant to be a robust, clean, and fault-tolerant
program -- it's meant to be a bare-bones implementation in four lines
of code ;).

Sure. The tutorial should be kept simple.
 

I had to read it a number of times before I got how you use the code, but its actually quite simple! Yes, this does, indeed, make sense. Thanks!
 
The above calculator object updates its internal resultBuffer only
upon full computation of a result, and hence its output (string
representation) is always going to be consistent. ANI/anic can't guess
which data the programmer intends to be atomic (and what not), since
in interactive, real-time applications, atomicity is a highly context-
dependent implementation decision. ANI does, however (I think) make it
easy to build systems that behave in whatever way the programmer
intends, which is the main purpose of a programming language in the
first place.

Of course. Automatically determining what should and shouldn't be atomic isn't something I'd expect of any language - if this is somehting we could do, we wouldn't need ANI, since automagically generating locks would be something other languages could do for us :D
 

Hopefully this helps!

> --
> Daniel Kersten.
> Leveraging dynamic paradigms since the synergies of 1985.

Daniel Kersten

unread,
Jul 26, 2010, 1:04:51 AM7/26/10
to mark...@gmail.com, anic
You mean that if I have multiple std.in's non of them fire until the input is flushed by a CR, and then all of them fire? That does make sense alright.

But it still leaves me with another set of questions: how can we handle consistency/synchronisation in the general case? How would we go about building our own queue until flushed mechanisms with ANI code? Or do we not need to? If not, why not?

On 26 July 2010 04:38, Mark Henderson <mark...@gmail.com> wrote:

Daniel Kersten

unread,
Jul 26, 2010, 4:05:22 AM7/26/10
to Ultimus Freelance, anic
Perhaps you culd write a kind of reference chart of language features and syntax? That way we can learn the changes from that and you won't have to write the entire tutorial yourself.
Reply all
Reply to author
Forward
0 new messages