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 https://groups.google.com/group/ats-lang-users
>>>>>>>>> .
>>>>>>>>> To view this discussion on the web visit 
>>>>>>>>> https://groups.google.com/d/msgid/ats-lang-u 
>>>>>>>>> <https://groups.google.com/d/msgid/ats-lang-users/716c8c61-d535-412d-8584-d4030d20801d%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/22469959-8769-400b-8bc4-b31ed6f5b6bf%40googlegroups.com.

Reply via email to