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.