If you use datavtype, then you do not have to deal with
views explicitly. It is just a style.
I myself tend to use datavtype when memory allocation is involved and
record (plus at-view) in an embedded setting where not memory allocation
is involved.
As for efficiency, there is no difference; internally, the same
representation is
used.
On Wednesday, March 8, 2017 at 8:11:06 AM UTC-5, August Alm wrote:
>
> In my original formulation of CSVState (not using a datavtype but a
> concrete vtypedef) I can add
>
> infix +=
> macdef(x, i) = ,(x) := ,(x) + ,(i)
>
> and then write
>
> st.2->tableRow += 1
>
> The best I can do with a datavtype is something like
>
> st.tableRow_inc(1)
>
> They are comparable to my eyes, as far as readability of code is concerned.
>
> How does my vtypedef-version compare with your datavtype-version in terms
> of efficiency? Is there a clear optimization reason to choose one over the
> other?
>
> Den onsdag 8 mars 2017 kl. 10:18:45 UTC+1 skrev August Alm:
>>
>> I see. Yes, being able to write
>>
>> st.tableRow(2)
>>
>> instead of
>>
>> st.2->tableRow := 2
>>
>> would make the code a little cleaner, I guess, What would really reduce
>> syntacic noise in my code though would be a slick way of writing what
>> currently I have as
>>
>> st.2->tableRow := st.2->tableRow + 1
>>
>> Continuing your suggestion, I can write something like
>>
>> extern fun {}
>> CSVState_update_tableRow (
>> st: !CSVState,
>> up: int -> int
>> ) : void
>>
>> implement {}
>> CSVState_update_tableRow(st, up) =
>> let val @CSVState(s) = st
>> in s.tableRow := up(s.tableRow); fold@(st)
>> end
>>
>> fun {}
>> plus(n: int): (int -> int) = lam(i) => n + i
>>
>> overload .tableRow with CSVState_update_tableRow
>>
>> After this I can write
>>
>> st.tableRow(plus(1))
>>
>> Is there a way to use overloading that would let me write that instead as
>>
>> st.tableRow(+1) ?
>>
>> I tried "overload + with plus" but get the error "operator fixity cannot
>> be resolved" when used.
>>
>>
>> Den onsdag 8 mars 2017 kl. 02:03:33 UTC+1 skrev gmhwxi:
>>>
>>> I was referring to some kind of code of the following style:
>>>
>>> typedef
>>> CSVState_rec =
>>> @{
>>> tableRow = int,
>>> tableCol = int,
>>> textRow = int,
>>> textCol = int
>>> }
>>>
>>> datavtype
>>> CSVState = CSVState of CSVState_rec
>>>
>>> extern
>>> fun{}
>>> CSVState_get_tableRow(!CSVState): int
>>> extern
>>> fun{}
>>> CSVState_set_tableRow(!CSVState, int): void
>>> overload .tableRow with CSVState_get_tableRow
>>> overload .tableRow with CSVState_set_tableRow
>>>
>>> implement
>>> {}
>>> CSVState_get_tableRow
>>> (state) = let
>>> //
>>> val+CSVState(x0) = state in x0.tableRow
>>> //
>>> end // end of [CSVState_get_tableRow]
>>> implement
>>> {}
>>> CSVState_set_tableRow
>>> (state, i0) = let
>>> //
>>> val+@CSVState(x0) = state in x0.tableRow := i0; fold@(state)
>>> //
>>> end // end of [CSVState_set_tableRow]
>>> typedef
>>> CSVState_rec =
>>> @{
>>> tableRow = int,
>>> tableCol = int,
>>> textRow = int,
>>> textCol = int
>>> }
>>>
>>> datavtype
>>> CSVState = CSVState of CSVState_rec
>>>
>>> extern
>>> fun{}
>>> CSVState_get_tableRow(!CSVState): int
>>> extern
>>> fun{}
>>> CSVState_set_tableRow(!CSVState, int): void
>>> overload .tableRow with CSVState_get_tableRow
>>> overload .tableRow with CSVState_set_tableRow
>>>
>>> implement
>>> {}
>>> CSVState_get_tableRow
>>> (state) = let
>>> //
>>> val+CSVState(x0) = state in x0.tableRow
>>> //
>>> end // end of [CSVState_get_tableRow]
>>> implement
>>> {}
>>> CSVState_set_tableRow
>>> (state, i0) = let
>>> //
>>> val+@CSVState(x0) = state in x0.tableRow := i0; fold@(state)
>>> //
>>> end // end of [CSVState_set_tableRow]
>>>
>>> On Tuesday, March 7, 2017 at 4:52:58 PM UTC-5, August Alm wrote:
>>>>
>>>> I'm glad too! I wrote my first "Hello World" program (in Haskell) less
>>>> than four months ago, before that I was completely illiterate about
>>>> programming--writing a linear, lazy CSV-parser in ATS has definitely been
>>>> my most challenging venture so far. I mean this in a good way. ATS is
>>>> quickly becoming my favorite language. It is daunting at times, sure, but
>>>> its unique combination of low-level abilities and functional abstractions
>>>> makes me feel like the Star Trek idiom "To boldly go where no one has gone
>>>> before", heh. The ATS sky is so vast I've almost forgot about monads. And
>>>> YES!, I do suggest trying ATS to every programmer I meet.
>>>>
>>>> Tangential to the topic of monads: Do you know if someone has thought
>>>> about the relations between ATS and "enriched effect calculus" (as
>>>> described in http://homepages.inf.ed.ac.uk/als/Research/Sources/eec.pdf)
>>>> or "linear state monads" (as mentioned in
>>>> https://arxiv.org/pdf/1403.1477.pdf)? There is a clear analogy.
>>>> Implementing a concept such as a linear state monad in ATS would be nice,
>>>> I
>>>> think. Monadic programming on an Arduino, anyone? =) It would certainly be
>>>> a unique selling point.
>>>>
>>>> I do not understand what you're aiming at with your suggestion to maje
>>>> CSVState a datavtype or absvtype. Could you elaborate? I have seen
>>>> abstract
>>>> types used as a way to make otherwise allowed operation illegal (there is
>>>> an example in your book, I think, of how to construct a record type where
>>>> some fields are mutable and some are not), but not for the sake of
>>>> overloading symbols.
>>>>
>>>> I will rewrite the code so that DELIM and QNLIN are passed as
>>>> templates. I also intend to add some further functionality, like functions
>>>> for filtering out errors, for printing and for collecting the output in
>>>> tabular form with rows and columns rather than as a single row. When I'm
>>>> satisfied I will make an npm-package out of it.
>>>>
>>>> Best wishes,
>>>> August
>>>>
>>>> Den tisdag 7 mars 2017 kl. 02:21:00 UTC+1 skrev gmhwxi:
>>>>>
>>>>> Really glad that you got it to work!
>>>>>
>>>>> I suggest that you make a npm-package for the parser and then
>>>>> publish the package. In this way, other ats-lang users can benefit
>>>>> from your work easily.
>>>>>
>>>>> You could try to introduce some abstract types into your code. For
>>>>> instance, I would suggest that you make CSVstate a datavtype (linear
>>>>> datatype)
>>>>> (a datatype is often referred to as being semi-abstract). Then you can
>>>>> introduce overloaded symbols for functions processing CSVstate, making
>>>>> your code
>>>>> more accessible.
>>>>>
>>>>> Also, the following interface:
>>>>>
>>>>> extern fun
>>>>> lex_csv(QNLIN: bool, DELIM: char, cs: llstring): CSVEntries
>>>>>
>>>>> can and probably should be changed into
>>>>>
>>>>> extern
>>>>> fun{}
>>>>> lex_csv(cs: listing): CSVEntries
>>>>>
>>>>> The parameters QNLIN and DELIM can be passed via templates:
>>>>>
>>>>> extern
>>>>> fun{} lex_csv$QNLIN(): char
>>>>> extern
>>>>> fun{} lex_csv$DELIM(): char
>>>>>
>>>>> implement{} lex_csv$QNLIN() = false
>>>>> implement{} lex_csv$DELIM() = ',' // default value
>>>>>
>>>>> Writing function templates (instead of functions) enables you to move
>>>>> your code around very conveniently. You can even move template code
>>>>> into the body of another function.
>>>>>
>>>>> That's all for now. Hope you will like ATS and tell/teach it to your
>>>>> friends.
>>>>>
>>>>> Cheers!
>>>>>
>>>>> On Monday, March 6, 2017 at 4:06:11 PM UTC-5, August Alm wrote:
>>>>>>
>>>>>> The code now seems to work as inteded!
>>>>>>
>>>>>> https://github.com/August-Alm/ats_csv_lexer
>>>>>>
>>>>>> Thank you for all the help. I still don't fully grokk why the
>>>>>> function needs to consume each of its arguments--will have to meditate
>>>>>> more
>>>>>> on that--but at least I know how to write code like this from now on.
>>>>>>
>>>>>> Den måndag 6 mars 2017 kl. 17:43:36 UTC+1 skrev gmhwxi:
>>>>>>>
>>>>>>> Yes, CSVstate needs to be changed as well.
>>>>>>>
>>>>>>> However, your code needs very little change. This is like a
>>>>>>> a 5 minute job to me. I would be happy to give it a try if you say
>>>>>>> so.
>>>>>>> But I thought that you might want to get the thrill of fixing the
>>>>>>> code :)
>>>>>>>
>>>>>>> On Monday, March 6, 2017 at 11:30:27 AM UTC-5, August Alm wrote:
>>>>>>>>
>>>>>>>> Hrrm, I had:
>>>>>>>>
>>>>>>>> fun
>>>>>>>> parse_entry
>>>>>>>> ( st: !CSVState >> _
>>>>>>>> , at: (int, int)
>>>>>>>> , acc: !$SBF.stringbuf
>>>>>>>> , cs: llstring
>>>>>>>> ) : stream_vt(CSVEntry)
>>>>>>>>
>>>>>>>> I gather I have to change not just [!$SBF.stringbuf] but also
>>>>>>>> [!CSVState >> _], right? What about if I did
>>>>>>>>
>>>>>>>> fun
>>>>>>>> parse_entry_con
>>>>>>>> ( st: !CSVState >> _
>>>>>>>> , at: (int, int)
>>>>>>>> , acc: !$SBF.stringbuf
>>>>>>>> , cs: llstring
>>>>>>>> ) : stream_vt_con(CSVEntry)
>>>>>>>>
>>>>>>>> and then put
>>>>>>>>
>>>>>>>> parse_entry(...) =
>>>>>>>> $ldelay
>>>>>>>> ( parse_entry_con(...)
>>>>>>>> , ( free(st)
>>>>>>>> ; free(acc)
>>>>>>>> ; free(cs)
>>>>>>>> )
>>>>>>>> )
>>>>>>>>
>>>>>>>> --would that work? Would it be idiomatic and efficient?
>>>>>>>>
>>>>>>>> Thanks, again,
>>>>>>>> August
>>>>>>>>
>>>>>>>> Den måndag 6 mars 2017 kl. 14:30:05 UTC+1 skrev gmhwxi:
>>>>>>>>>
>>>>>>>>> I forgot to tell you something essential in using stream_vt.
>>>>>>>>> The following interface for 'test' cannot work:
>>>>>>>>>
>>>>>>>>> fun test (acc: !$SBF.stringbuf, cs: llstring): stream_vt(DT) =
>>>>>>>>>
>>>>>>>>> What you need is
>>>>>>>>>
>>>>>>>>> fun test (acc: $SBF.stringbuf, cs: llstring): stream_vt(DT) =
>>>>>>>>>
>>>>>>>>> The 'acc' stringbuf needs to be consumed by 'test'. The
>>>>>>>>> implementation
>>>>>>>>> of 'test' looks like this:
>>>>>>>>>
>>>>>>>>> $ldelay
>>>>>>>>> (
>>>>>>>>> <code for stream construction>
>>>>>>>>> ,
>>>>>>>>> (freeing(acc); freeing(cs)) // this part is executed when the
>>>>>>>>> stream is freed
>>>>>>>>> )
>>>>>>>>>
>>>>>>>>> On Mon, Mar 6, 2017 at 8:19 AM, August Alm <[email protected]>
>>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>>> The points you mention are part of the reason I chose to wrote
>>>>>>>>>> the csv lexer the way I did. It follows one of the fastests Haskell
>>>>>>>>>> csv
>>>>>>>>>> parsers, and I was curious to see how using linear types could
>>>>>>>>>> optimize
>>>>>>>>>> performance.
>>>>>>>>>>
>>>>>>>>>> Regarding your suggestion on how to make better use of $ldelay in
>>>>>>>>>> my code: I'm stuck on a compiler error that I can't make sense of.
>>>>>>>>>> The
>>>>>>>>>> following pseudo-minimal example throws the same kind of errors:
>>>>>>>>>>
>>>>>>>>>> #include "share/atspre_define.hats"
>>>>>>>>>> #include "share/atspre_staload.hats"
>>>>>>>>>> staload UN = "prelude/SATS/unsafe.sats"
>>>>>>>>>> staload SBF = "libats/SATS/stringbuf.sats"
>>>>>>>>>> staload _(*SBF*) = "libats/DATS/stringbuf.dats"
>>>>>>>>>>
>>>>>>>>>> datatype DT = D_T of @{ alpha = char }
>>>>>>>>>> vtypedef llstring = stream_vt(char)
>>>>>>>>>>
>>>>>>>>>> fun
>>>>>>>>>> test (acc: !$SBF.stringbuf, cs: llstring): stream_vt(DT)
>>>>>>>>>> =
>>>>>>>>>> $ldelay
>>>>>>>>>> ( case !cs of
>>>>>>>>>> | ~stream_vt_nil() =>
>>>>>>>>>> if $SBF.stringbuf_get_size(acc) = i2sz(0) then
>>>>>>>>>> stream_vt_nil()
>>>>>>>>>> else stream_vt_cons(D_T(@{alpha = 'a'}),
>>>>>>>>>> stream_vt_make_nil())
>>>>>>>>>> | ~stream_vt_cons(c, cs1) =>
>>>>>>>>>> let val crec = D_T(@{alpha = c})
>>>>>>>>>> in stream_vt_cons(crec, test(acc, cs1))
>>>>>>>>>> end
>>>>>>>>>> , ~cs
>>>>>>>>>> )
>>>>>>>>>>
>>>>>>>>>> The compiler can not infer the type I want (which is
>>>>>>>>>> [stream_vt_con(DT)] for the [stream_vt_nil()] following the first
>>>>>>>>>> [then] in
>>>>>>>>>> the function body. The error message says
>>>>>>>>>>
>>>>>>>>>> the dynamic expression cannot be assigned the type [S2EVar(5492)].
>>>>>>>>>> [...] mismatch of sorts in unification:
>>>>>>>>>> The sort of variable is: S2RTbas(S2RTBASimp(1; t@ype))
>>>>>>>>>> The sort of solution is: S2RTbas(S2RTBASimp(2; viewtype))
>>>>>>>>>> [...] mismatch of static terms (tyleq):
>>>>>>>>>> The actual term is: S2Eapp(S2Ecst(stream_vt_con); S2EVar(5495))
>>>>>>>>>> The needed term is: S2EVar(5492)
>>>>>>>>>>
>>>>>>>>>> (There are further errors of the same form.) Is the culprit that
>>>>>>>>>> [stream_vt] of a nonlinear datatype requires some special care? The
>>>>>>>>>> version
>>>>>>>>>> with [stream_vt_make_nil()] instead of explicit [$ldelay] works so
>>>>>>>>>> the
>>>>>>>>>> error ought to be subtle.
>>>>>>>>>>
>>>>>>>>>> Best wishes,
>>>>>>>>>> August
>>>>>>>>>>
>>>>>>>>>> Den söndag 5 mars 2017 kl. 23:58:35 UTC+1 skrev gmhwxi:
>>>>>>>>>>>
>>>>>>>>>>> Yes, you definitely got it :)
>>>>>>>>>>>
>>>>>>>>>>> Stream_vt is very memory-frugal.
>>>>>>>>>>>
>>>>>>>>>>> Haskell relies on deforestation (complex complier optimization)
>>>>>>>>>>> to reduce memory usage of lazy evaluation. In ATS, deforestation
>>>>>>>>>>> is
>>>>>>>>>>> not supported. Instead, the programmer needs to recycle memory
>>>>>>>>>>> explicitly.
>>>>>>>>>>>
>>>>>>>>>>> Compared to Haskell, corresponding code using stream_vt in ATS
>>>>>>>>>>> can be
>>>>>>>>>>> much more efficient both time-wise and memory-wise.
>>>>>>>>>>>
>>>>>>>>>>> For instance, the following example (for computing Mersenne
>>>>>>>>>>> primes) can
>>>>>>>>>>> run for days without run-time GC:
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> https://github.com/githwxi/ATS-Postiats/blob/master/doc/EXAMPLE/RosettaCode/Lucas-Lehmer_test2.dats
>>>>>>>>>>>
>>>>>>>>>>> It convincingly attests to the power of linear streams.
>>>>>>>>>>>
>>>>>>>>>>> Cheers!
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On Sun, Mar 5, 2017 at 5:34 PM, August Alm <[email protected]>
>>>>>>>>>>> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> Thanks for the tip! I think I understand. I treated $ldelay
>>>>>>>>>>>> much as a data constructor, so that all streams are equally lazy,
>>>>>>>>>>>> whereas
>>>>>>>>>>>> there are in fact many ways to sequence into thunks. Let me give
>>>>>>>>>>>> an example
>>>>>>>>>>>> to anchor the discussion. Both the following implementations of a
>>>>>>>>>>>> map-template for linear streams typecheck:
>>>>>>>>>>>>
>>>>>>>>>>>> fun {a, b: t0ype}
>>>>>>>>>>>> map_make_cons
>>>>>>>>>>>> ( xs: stream_vt(a)
>>>>>>>>>>>> , f: a -> b
>>>>>>>>>>>> ) : stream_vt(b) =
>>>>>>>>>>>> case !xs of
>>>>>>>>>>>> | ~stream_vt_nil() => stream_vt_make_nil()
>>>>>>>>>>>> | ~stream_vt_cons(x, xs1) =>
>>>>>>>>>>>> stream_vt_make_cons(f(x), map_make_cons(xs1, f))
>>>>>>>>>>>>
>>>>>>>>>>>> fun {a, b: t0ype}
>>>>>>>>>>>> map_ldelay
>>>>>>>>>>>> ( xs: stream_vt(a)
>>>>>>>>>>>> , f: a -> b
>>>>>>>>>>>> ) : stream_vt(b) =
>>>>>>>>>>>> $ldelay
>>>>>>>>>>>> ( case !xs of
>>>>>>>>>>>> | ~stream_vt_nil() => stream_vt_nil()
>>>>>>>>>>>> | ~stream_vt_cons(x, xs1) =>
>>>>>>>>>>>> stream_vt_cons(f(x), map_ldelay(xs1, f))
>>>>>>>>>>>> , ~xs
>>>>>>>>>>>> )
>>>>>>>>>>>>
>>>>>>>>>>>> The second is maximally lazy. The first, [map_make_cons] is
>>>>>>>>>>>> less lazy because checking the case-conditions is not delayed. My
>>>>>>>>>>>> code was
>>>>>>>>>>>> like the first example, only much more was going on inside the
>>>>>>>>>>>> case
>>>>>>>>>>>> expressions. Is that a correct assessment?
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Den söndag 5 mars 2017 kl. 04:07:42 UTC+1 skrev gmhwxi:
>>>>>>>>>>>>>
>>>>>>>>>>>>> BTW, it seems you don't need to do much to fix the issue.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Basically, you just do
>>>>>>>>>>>>>
>>>>>>>>>>>>> 1) Put the body of parse_entry into $ldelay(...)
>>>>>>>>>>>>> 2) Change stream_vt_make_cons into stream_vt_cons
>>>>>>>>>>>>>
>>>>>>>>>>>>> There may be a few other things but they should all be
>>>>>>>>>>>>> very minor.
>>>>>>>>>>>>>
>>>>>>>>>>>>> On Saturday, March 4, 2017 at 9:47:07 PM UTC-5, gmhwxi wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I took a glance at your code.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I noticed a very common mistake involving the use of
>>>>>>>>>>>>>> stream (or stream_vt). Basically, the way stream is used
>>>>>>>>>>>>>> in your code is like the way list is used. This causes the
>>>>>>>>>>>>>> stack issue you encountered.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Say that you have a function that returns a stream. In nearly
>>>>>>>>>>>>>> all cases, the correct way to implement such a function should
>>>>>>>>>>>>>> use the following style:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> fun foo(...): stream_vt(...) = $ldelay
>>>>>>>>>>>>>> (
>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>> )
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> The idea is that 'foo' should return in O(1) time. The body
>>>>>>>>>>>>>> of $ldelay
>>>>>>>>>>>>>> is only evaluated with the first element of the returned
>>>>>>>>>>>>>> stream is neede.
>>>>>>>>>>>>>> Sometimes, this is call full laziness. Without full laziness,
>>>>>>>>>>>>>> a stream may
>>>>>>>>>>>>>> behave like a list, defeating the very purpose of using a
>>>>>>>>>>>>>> stream.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On Saturday, March 4, 2017 at 7:27:03 PM UTC-5, August Alm
>>>>>>>>>>>>>> wrote:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> I've spent few hours trying to figure out how to make
>>>>>>>>>>>>>>> proper use of npm and gave up--for now. If the project turns
>>>>>>>>>>>>>>> into something
>>>>>>>>>>>>>>> more serious (i.e., useful to others) then I will have another
>>>>>>>>>>>>>>> go at it.
>>>>>>>>>>>>>>> For now my naive attempts at making effective use of linear
>>>>>>>>>>>>>>> streams can be
>>>>>>>>>>>>>>> witnessed at GitHub:
>>>>>>>>>>>>>>> https://github.com/August-Alm/ats_csv_lexer Any and all
>>>>>>>>>>>>>>> comments on how to improve are appreciated.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Best wishes, August.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Den fredag 3 mars 2017 kl. 23:57:54 UTC+1 skrev gmhwxi:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> One possibility is to build a npm package and then publish
>>>>>>>>>>>>>>>> it.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> If you go to https://www.npmjs.com/ and seach for
>>>>>>>>>>>>>>>> 'atscntrb'. You can find
>>>>>>>>>>>>>>>> plenty packages. You may need to install npm first.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> If you do build a npm package, I suggest that you choose a
>>>>>>>>>>>>>>>> name space for
>>>>>>>>>>>>>>>> yourself. E.g., atscntrb-a?a-..., where ? is the first
>>>>>>>>>>>>>>>> letter of your middle name.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> On Fri, Mar 3, 2017 at 5:48 PM, August Alm <
>>>>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> How would I best share larger code portions? I have no
>>>>>>>>>>>>>>>>> concerns about my making my mistakes public, heh.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> I believe everything is lazy as-is (all data is
>>>>>>>>>>>>>>>>> [stream_vt("sometype")]). And I've tried to write
>>>>>>>>>>>>>>>>> tail-recursive functional
>>>>>>>>>>>>>>>>> code. The algorithm is based on two mutually recursing
>>>>>>>>>>>>>>>>> functions, "fun ...
>>>>>>>>>>>>>>>>> and ..", similar to how you did things in your csv-parser
>>>>>>>>>>>>>>>>> (thanks for
>>>>>>>>>>>>>>>>> pointing out that piece of code). However, I cannot set them
>>>>>>>>>>>>>>>>> up with "fn*
>>>>>>>>>>>>>>>>> .. and .." to enforce a local jump because they call each
>>>>>>>>>>>>>>>>> other in a too
>>>>>>>>>>>>>>>>> intertwined way. Might that be it?
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Den fredag 3 mars 2017 kl. 23:32:15 UTC+1 skrev gmhwxi:
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> You are welcome!
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Since I have not seen your code, I could only guess :)
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Usually, what you described can be fixed by using
>>>>>>>>>>>>>>>>>> tail-recursion, or
>>>>>>>>>>>>>>>>>> by using lazy-evaluation. The former approach is
>>>>>>>>>>>>>>>>>> straightforward. You
>>>>>>>>>>>>>>>>>> just need to identify the function or functions that
>>>>>>>>>>>>>>>>>> cause the deep stack
>>>>>>>>>>>>>>>>>> usage. Then try to rewrite using tail-recursion.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> On Fri, Mar 3, 2017 at 5:25 PM, August Alm <
>>>>>>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Hi!
>>>>>>>>>>>>>>>>>>> I had indeed made a logical error that caused any stream
>>>>>>>>>>>>>>>>>>> with "carriage return" followed by "newline" to recurse
>>>>>>>>>>>>>>>>>>> indefinitely. Thank
>>>>>>>>>>>>>>>>>>> you for your patience and pedagogical instincts, Professor!
>>>>>>>>>>>>>>>>>>> There is still
>>>>>>>>>>>>>>>>>>> some issue though, one that I believe is more subtle. I
>>>>>>>>>>>>>>>>>>> fixed the logical
>>>>>>>>>>>>>>>>>>> error and my algorithm now handles all the test cases you
>>>>>>>>>>>>>>>>>>> suggested.
>>>>>>>>>>>>>>>>>>> However, when fed an actual CSV-file with a thousand rows
>>>>>>>>>>>>>>>>>>> and about 300
>>>>>>>>>>>>>>>>>>> columns it still segfaults--unless I manually increase the
>>>>>>>>>>>>>>>>>>> stack space on
>>>>>>>>>>>>>>>>>>> my computer! I don't know exactly where the critical limit
>>>>>>>>>>>>>>>>>>> is, but
>>>>>>>>>>>>>>>>>>> increasing it from 8192 kbytes to 65536 certainly did the
>>>>>>>>>>>>>>>>>>> trick. The whole
>>>>>>>>>>>>>>>>>>> file parsed without problem, and rather quickly at that. It
>>>>>>>>>>>>>>>>>>> seems my
>>>>>>>>>>>>>>>>>>> algorithm makes too much use of stack allocation and that I
>>>>>>>>>>>>>>>>>>> may have to
>>>>>>>>>>>>>>>>>>> rethink some of my (would-be) optimization choices.
>>>>>>>>>>>>>>>>>>> Best wishes,
>>>>>>>>>>>>>>>>>>> August
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Den fredag 3 mars 2017 kl. 15:22:00 UTC+1 skrev gmhwxi:
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Now you may do the following tests:
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Try:
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> val ins = streamize_string_char("a;b") // should work
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Try:
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> val ins = streamize_string_char("a;b\n") // may not work
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Try:
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> val ins = streamize_string_char("a;b\015\012") //
>>>>>>>>>>>>>>>>>>>> should cause crash
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> On Thursday, March 2, 2017 at 9:21:21 PM UTC-5, gmhwxi
>>>>>>>>>>>>>>>>>>>> wrote:
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> When tried, I saw the following 5 chars (ascii) in
>>>>>>>>>>>>>>>>>>>>> small.csv:
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> 97
>>>>>>>>>>>>>>>>>>>>> 59
>>>>>>>>>>>>>>>>>>>>> 98
>>>>>>>>>>>>>>>>>>>>> 13
>>>>>>>>>>>>>>>>>>>>> 10
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> My testing code:
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> #include"share/atspre_staload.hats"
>>>>>>>>>>>>>>>>>>>>> #include"share/HATS/atspre_staload_libats_ML.hats"
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> implement main0 () = {
>>>>>>>>>>>>>>>>>>>>> val inp = fileref_open_exn("small.csv", file_mode_r)
>>>>>>>>>>>>>>>>>>>>> val ins = streamize_fileref_char(inp)
>>>>>>>>>>>>>>>>>>>>> val ins = stream2list_vt(ins)
>>>>>>>>>>>>>>>>>>>>> val ins = g0ofg1(list_vt2t(ins))97
>>>>>>>>>>>>>>>>>>>>> val ( ) = println! ("length(ins) = ", length(ins))
>>>>>>>>>>>>>>>>>>>>> val ( ) = (ins).foreach()(lam c =>
>>>>>>>>>>>>>>>>>>>>> println!(char2int0(c)))
>>>>>>>>>>>>>>>>>>>>> (*
>>>>>>>>>>>>>>>>>>>>> val lexed = lex_csv(true, ';', ins)
>>>>>>>>>>>>>>>>>>>>> *)
>>>>>>>>>>>>>>>>>>>>> val () = fileref_close(inp)
>>>>>>>>>>>>>>>>>>>>> (*
>>>>>>>>>>>>>>>>>>>>> val h = (lexed.head())
>>>>>>>>>>>>>>>>>>>>> val- CSV_Field(r) = h
>>>>>>>>>>>>>>>>>>>>> val a = r.csvFieldContent
>>>>>>>>>>>>>>>>>>>>> val () = println!(a)
>>>>>>>>>>>>>>>>>>>>> *)
>>>>>>>>>>>>>>>>>>>>> }
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> On Thu, Mar 2, 2017 at 9:13 PM, August Alm <...>
>>>>>>>>>>>>>>>>>>>>> wrote:
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> Just "a;b", or? (Attached.)
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> Den fredag 3 mars 2017 kl. 03:03:08 UTC+1 skrev
>>>>>>>>>>>>>>>>>>>>>> gmhwxi:
>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>> I suspect that the file you used contains other
>>>>>>>>>>>>>>>>>>>>>>> characters.
>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>> What is in "small.csv"?
>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>> On Thu, Mar 2, 2017 at 8:52 PM, August Alm <...>
>>>>>>>>>>>>>>>>>>>>>>> wrote:
>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> The file compiles (I've tried a few compiler
>>>>>>>>>>>>>>>>>>>>>>>> options) and "gdb run" yields
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> Program received signal SIGSEGV, Segmentation
>>>>>>>>>>>>>>>>>>>>>>>> fault.
>>>>>>>>>>>>>>>>>>>>>>>> 0x00007ffff783eea5 in _int_malloc
>>>>>>>>>>>>>>>>>>>>>>>> (av=0x7ffff7b6a620 <main_arena>, bytes=16) at
>>>>>>>>>>>>>>>>>>>>>>>> malloc.c:3790
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> The frames 0-3 involve allocation functions that
>>>>>>>>>>>>>>>>>>>>>>>> are not particular to my file. Frame 4 says:
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> #4 __patsfun_28__28__14 (arg0=<optimized out>,
>>>>>>>>>>>>>>>>>>>>>>>> env1=0x605540, env0=10 '\n') at csv_lexer_dats.c:9023
>>>>>>>>>>>>>>>>>>>>>>>> 9023 ATSINSmove_con1_new(tmpret63__14,
>>>>>>>>>>>>>>>>>>>>>>>> postiats_tysum_7) ;
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> My not-so-educated guess is that this refers to
>>>>>>>>>>>>>>>>>>>>>>>> making a cons-cell of a stream.
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> But: How can my function do just fine when manually
>>>>>>>>>>>>>>>>>>>>>>>> fed
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> cons('a', cons( ';', sing('b'))):
>>>>>>>>>>>>>>>>>>>>>>>> stream_vt(char),
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> but segfault when I use [streamize_fileref_char] to
>>>>>>>>>>>>>>>>>>>>>>>> construct the very same stream from the string "a;b"
>>>>>>>>>>>>>>>>>>>>>>>> in a file? Where is
>>>>>>>>>>>>>>>>>>>>>>>> the room for an infinite recursion in that?
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> Thank you,
>>>>>>>>>>>>>>>>>>>>>>>> August
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> Den torsdag 2 mars 2017 kl. 23:04:35 UTC+1 skrev
>>>>>>>>>>>>>>>>>>>>>>>> August Alm:
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> Hi!
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> I'm in over my head and tried writing a CSV-parser
>>>>>>>>>>>>>>>>>>>>>>>>> using linear lazy streams. My code thus far is 600
>>>>>>>>>>>>>>>>>>>>>>>>> lines and almost to my
>>>>>>>>>>>>>>>>>>>>>>>>> own surprise I get it to compile! However, there is
>>>>>>>>>>>>>>>>>>>>>>>>> something fishy because
>>>>>>>>>>>>>>>>>>>>>>>>> I get a segfault when applying my program to an
>>>>>>>>>>>>>>>>>>>>>>>>> actual CSV-file. I've been
>>>>>>>>>>>>>>>>>>>>>>>>> trying to debug using gdb but the fault eludes me.
>>>>>>>>>>>>>>>>>>>>>>>>> Since I don't expect
>>>>>>>>>>>>>>>>>>>>>>>>> anyone to mull through 600 lines of code, I am hoping
>>>>>>>>>>>>>>>>>>>>>>>>> these code snippets
>>>>>>>>>>>>>>>>>>>>>>>>> are enough for one of you guys to give me some advice.
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> This code executes just fine:
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> implement main0 () = {
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> val test = stream_vt_make_cons(
>>>>>>>>>>>>>>>>>>>>>>>>> 'a',
>>>>>>>>>>>>>>>>>>>>>>>>> stream_vt_make_cons(
>>>>>>>>>>>>>>>>>>>>>>>>> ';',
>>>>>>>>>>>>>>>>>>>>>>>>> stream_vt_make_sing('b'))) (* the stream
>>>>>>>>>>>>>>>>>>>>>>>>> ('a', ';', 'b') *)
>>>>>>>>>>>>>>>>>>>>>>>>> val lexed = lex_csv(true, ';', test)
>>>>>>>>>>>>>>>>>>>>>>>>> val h = (lexed.head())
>>>>>>>>>>>>>>>>>>>>>>>>> val- CSV_Field(r) = h
>>>>>>>>>>>>>>>>>>>>>>>>> val a = r.csvFieldContent
>>>>>>>>>>>>>>>>>>>>>>>>> val () = println!(a)
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> }
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> Here [lex_csv] is my 600-line alogrithm. It reads
>>>>>>>>>>>>>>>>>>>>>>>>> a [stream_vt(char)] and gives back a
>>>>>>>>>>>>>>>>>>>>>>>>> [stream_vt(CSVEntry)], where
>>>>>>>>>>>>>>>>>>>>>>>>> [CSVEntry] is a record type, one of whose fields is
>>>>>>>>>>>>>>>>>>>>>>>>> [CSVFieldContent]. When
>>>>>>>>>>>>>>>>>>>>>>>>> executing the program I get "a" printed to the
>>>>>>>>>>>>>>>>>>>>>>>>> console.
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> This code results in a segfault:
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> implement main0 () = {
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> val inp = fileref_open_exn("small.csv",
>>>>>>>>>>>>>>>>>>>>>>>>> file_mode_r)
>>>>>>>>>>>>>>>>>>>>>>>>> val ins = streamize_fileref_char(inp)
>>>>>>>>>>>>>>>>>>>>>>>>> val lexed = lex_csv(true, ';', ins)
>>>>>>>>>>>>>>>>>>>>>>>>> val () = fileref_close(inp)
>>>>>>>>>>>>>>>>>>>>>>>>> val h = (lexed.head())
>>>>>>>>>>>>>>>>>>>>>>>>> val- CSV_Field(r) = h
>>>>>>>>>>>>>>>>>>>>>>>>> val a = r.csvFieldContent
>>>>>>>>>>>>>>>>>>>>>>>>> val () = println!(a)
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> }
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> The file "small.csv" only contains the string
>>>>>>>>>>>>>>>>>>>>>>>>> "a;b". Hence I would expect this code to give the
>>>>>>>>>>>>>>>>>>>>>>>>> result as the previous
>>>>>>>>>>>>>>>>>>>>>>>>> one! But, it doesn't just return something else, it
>>>>>>>>>>>>>>>>>>>>>>>>> segfaults.
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> gdb indicates there is a malloc problem having to
>>>>>>>>>>>>>>>>>>>>>>>>> do with "GC_clear_stack_inner", in case that's
>>>>>>>>>>>>>>>>>>>>>>>>> helpful. (I'm a
>>>>>>>>>>>>>>>>>>>>>>>>> mathematician who recently left academia after
>>>>>>>>>>>>>>>>>>>>>>>>> postdoc and decided to teach
>>>>>>>>>>>>>>>>>>>>>>>>> myself programming to become more useful outside of
>>>>>>>>>>>>>>>>>>>>>>>>> academia; hence I
>>>>>>>>>>>>>>>>>>>>>>>>> understand type systems and the like--the mathy
>>>>>>>>>>>>>>>>>>>>>>>>> stuff--a lot better than I
>>>>>>>>>>>>>>>>>>>>>>>>> understand memory allocation and other stuff that
>>>>>>>>>>>>>>>>>>>>>>>>> most programmers are
>>>>>>>>>>>>>>>>>>>>>>>>> supposed to be confident with.)
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> What could be the problem here?
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> Best wishes,
>>>>>>>>>>>>>>>>>>>>>>>>> August
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>>>>>>>>>>> You received this message because you are
>>>>>>>>>>>>>>>>>>>>>>>> subscribed to the Google Groups "ats-lang-users" group.
>>>>>>>>>>>>>>>>>>>>>>>> To unsubscribe from this group and stop receiving
>>>>>>>>>>>>>>>>>>>>>>>> emails from it, send an email to
>>>>>>>>>>>>>>>>>>>>>>>> [email protected].
>>>>>>>>>>>>>>>>>>>>>>>> To post to this group, send email to
>>>>>>>>>>>>>>>>>>>>>>>> [email protected].
>>>>>>>>>>>>>>>>>>>>>>>> Visit this group at
>>>>>>>>>>>>>>>>>>>>>>>> https://groups.google.com/group/ats-lang-users.
>>>>>>>>>>>>>>>>>>>>>>>> To view this discussion on the web visit
>>>>>>>>>>>>>>>>>>>>>>>> https://groups.google.com/d/msgid/ats-lang-users/69535c5c-eac3-472c-bb39-062ad4708a72%40googlegroups.com
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> <https://groups.google.com/d/msgid/ats-lang-users/69535c5c-eac3-472c-bb39-062ad4708a72%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>>>>>>>>>>>>>>>>>>> .
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>>>>>>>>> You received this message because you are subscribed
>>>>>>>>>>>>>>>>>>>>>> to the Google Groups "ats-lang-users" group.
>>>>>>>>>>>>>>>>>>>>>> To unsubscribe from this group and stop receiving
>>>>>>>>>>>>>>>>>>>>>> emails from it, send an email to
>>>>>>>>>>>>>>>>>>>>>> [email protected].
>>>>>>>>>>>>>>>>>>>>>> To post to this group, send email to
>>>>>>>>>>>>>>>>>>>>>> [email protected].
>>>>>>>>>>>>>>>>>>>>>> Visit this group at
>>>>>>>>>>>>>>>>>>>>>> https://groups.google.com/group/ats-lang-users.
>>>>>>>>>>>>>>>>>>>>>> To view this discussion on the web visit
>>>>>>>>>>>>>>>>>>>>>> https://groups.google.com/d/msgid/ats-lang-users/e608c7bb-42ce-457b-a606-9fe3525f801d%40googlegroups.com
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> <https://groups.google.com/d/msgid/ats-lang-users/e608c7bb-42ce-457b-a606-9fe3525f801d%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>>>>>>>>>>>>>>>>> .
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>>>>>> You received this message because you are subscribed to
>>>>>>>>>>>>>>>>>>> the Google Groups "ats-lang-users" group.
>>>>>>>>>>>>>>>>>>> To unsubscribe from this group and stop receiving emails
>>>>>>>>>>>>>>>>>>> from it, send an email to
>>>>>>>>>>>>>>>>>>> [email protected].
>>>>>>>>>>>>>>>>>>> To post to this group, send email to
>>>>>>>>>>>>>>>>>>> [email protected].
>>>>>>>>>>>>>>>>>>> Visit this group at
>>>>>>>>>>>>>>>>>>> https://groups.google.com/group/ats-lang-users.
>>>>>>>>>>>>>>>>>>> To view this discussion on the web visit
>>>>>>>>>>>>>>>>>>> https://groups.google.com/d/msgid/ats-lang-users/34dfad01-9bd4-464f-9ccd-6dfae8207f4c%40googlegroups.com
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> <https://groups.google.com/d/msgid/ats-lang-users/34dfad01-9bd4-464f-9ccd-6dfae8207f4c%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>>>>>>>>>>>>>> .
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>>>> You received this message because you are subscribed to
>>>>>>>>>>>>>>>>> the Google Groups "ats-lang-users" group.
>>>>>>>>>>>>>>>>> To unsubscribe from this group and stop receiving emails
>>>>>>>>>>>>>>>>> from it, send an email to
>>>>>>>>>>>>>>>>> [email protected].
>>>>>>>>>>>>>>>>> To post to this group, send email to
>>>>>>>>>>>>>>>>> [email protected].
>>>>>>>>>>>>>>>>> Visit this group at
>>>>>>>>>>>>>>>>> https://groups.google.com/group/ats-lang-users.
>>>>>>>>>>>>>>>>> To view this discussion on the web visit
>>>>>>>>>>>>>>>>> https://groups.google.com/d/msgid/ats-lang-users/c2f9d2b7-61f5-4142-b8b2-930147ee589d%40googlegroups.com
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> <https://groups.google.com/d/msgid/ats-lang-users/c2f9d2b7-61f5-4142-b8b2-930147ee589d%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>>>>>>>>>>>> .
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> --
>>>>>>>>>>>> You received this message because you are subscribed to the
>>>>>>>>>>>> Google Groups "ats-lang-users" group.
>>>>>>>>>>>> To unsubscribe from this group and stop receiving emails from
>>>>>>>>>>>> it, send an email to [email protected].
>>>>>>>>>>>> To post to this group, send email to
>>>>>>>>>>>> [email protected].
>>>>>>>>>>>> Visit this group at
>>>>>>>>>>>> https://groups.google.com/group/ats-lang-users.
>>>>>>>>>>>> To view this discussion on the web visit
>>>>>>>>>>>> https://groups.google.com/d/msgid/ats-lang-users/d78409e2-aff1-4b96-98f3-eb3a5d20ff95%40googlegroups.com
>>>>>>>>>>>>
>>>>>>>>>>>> <https://groups.google.com/d/msgid/ats-lang-users/d78409e2-aff1-4b96-98f3-eb3a5d20ff95%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>>>>>>> .
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> --
>>>>>>>>>> You received this message because you are subscribed to the
>>>>>>>>>> Google Groups "ats-lang-users" group.
>>>>>>>>>> To unsubscribe from this group and stop receiving emails from it,
>>>>>>>>>> send an email to [email protected].
>>>>>>>>>> To post to this group, send email to [email protected].
>>>>>>>>>> Visit this group at <a href="
>>>>>>>>>> https://groups.google.com/group/ats-lang-users" rel="nofollow"
>>>>>>>>>> target="_blank" onmousedown="this.href='
>>>>>>>>>> https://groups.google.com/group/ats-lang-users';return true;"
>>>>>>>>>> onclick="this.href='
>>>>>>>>>> https://groups.google.com/group/ats-lang-users';return tr
>>>>>>>>>
>>>>>>>>>
--
You received this message because you are subscribed to the Google Groups
"ats-lang-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/ats-lang-users.
To view this discussion on the web visit
https://groups.google.com/d/msgid/ats-lang-users/5588e888-3904-46c6-b851-2c3548fa6f27%40googlegroups.com.