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 <augu...@gmail.com> >>>>>>>>> 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 <augu...@gmail.com> >>>>>>>>>>> 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 < >>>>>>>>>>>>>>>> augu...@gmail.com> 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 < >>>>>>>>>>>>>>>>>> augu...@gmail.com> 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 >>>>>>>>>>>>>>>>>>>>>>>> ats-lang-user...@googlegroups.com. >>>>>>>>>>>>>>>>>>>>>>>> To post to this group, send email to >>>>>>>>>>>>>>>>>>>>>>>> ats-lan...@googlegroups.com. >>>>>>>>>>>>>>>>>>>>>>>> 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 >>>>>>>>>>>>>>>>>>>>>> ats-lang-user...@googlegroups.com. >>>>>>>>>>>>>>>>>>>>>> To post to this group, send email to >>>>>>>>>>>>>>>>>>>>>> ats-lan...@googlegroups.com. >>>>>>>>>>>>>>>>>>>>>> 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 >>>>>>>>>>>>>>>>>>> ats-lang-user...@googlegroups.com. >>>>>>>>>>>>>>>>>>> To post to this group, send email to >>>>>>>>>>>>>>>>>>> ats-lan...@googlegroups.com. >>>>>>>>>>>>>>>>>>> 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 >>>>>>>>>>>>>>>>> ats-lang-user...@googlegroups.com. >>>>>>>>>>>>>>>>> To post to this group, send email to >>>>>>>>>>>>>>>>> ats-lan...@googlegroups.com. >>>>>>>>>>>>>>>>> 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 ats-lang-user...@googlegroups.com. >>>>>>>>>>>> To post to this group, send email to >>>>>>>>>>>> ats-lan...@googlegroups.com. >>>>>>>>>>>> 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 ats-lang-user...@googlegroups.com. >>>>>>>>>> To post to this group, send email to ats-lan...@googlegroups.com. >>>>>>>>>> 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 ats-lang-users+unsubscr...@googlegroups.com. To post to this group, send email to ats-lang-users@googlegroups.com. 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.