Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rejoin redesign #154

Open
loziniak opened this issue Nov 6, 2023 · 12 comments
Open

rejoin redesign #154

loziniak opened this issue Nov 6, 2023 · 12 comments

Comments

@loziniak
Copy link

loziniak commented Nov 6, 2023

I tried rejoin on some different combinations of values, and it gives some unexpected results for me, given a description, that it "joins a block of values":

>> rejoin [[2 3][4 5]]
== [2 3 [4 5]]
>> rejoin [<tag1> <tag2>]
== <tag1<tag2>>
>> rejoin [quote (2 3) quote (4 5)]
== (2 3 (4 5))

I'd expect it to work like that:

>> rejoin [[2 3][4 5]]
== [2 3 4 5]                    ; OK
>> rejoin [<tag1> <tag2>]
== <tag1tag2>                   ; OK
>> rejoin [quote (2 3) quote (4 5)]
== (2 3 4 5)                    ; OK

And there is also a strange behavior with paths:

>> p: rejoin [quote a/b quote c/d]
== a/b/c/d
>> a: [b [c [d 10]]]
== [b [c [d 10]]]
>> get p
== none                    ; p is unexpectedly broken
>> p2: to path! [a b c d]
== a/b/c/d
>> get p2
== 10                      ; ...and should work like p2
>> to block! p
== [a b c/d]               ; something is wrong
>> to block! p2
== [a b c d]
@hiiamboris
Copy link

tag1<tag2>> is a problem of tags, not of rejoin. And in fairness the block/path issue can be fixed by rewriting the docstring of rejoin. So what are you proposing in the end?

@loziniak
Copy link
Author

loziniak commented Nov 6, 2023

I see rejoin like this:

rejoin: func [
    "Reduces and joins a block of values." 
    block [block!] "Values to reduce and join"
] [
    if empty? block: reduce block [return block]
    joined: either series? first block [copy first block] [to string! first block]
    while [not empty? block: next block] [
    	append joined first block
    ]
    joined
]

And I'd get over the tags, as well as emails and urls.

@loziniak
Copy link
Author

loziniak commented Nov 6, 2023

Good to keep an eye on #134 too.

@hiiamboris
Copy link

hiiamboris commented Nov 6, 2023

I see rejoin like this:

Given its importance, this won't cut, because:

;; minimal code
>> clock/times [append form 1 [2 3 4 5 6 7 8 9 10]] 1e5
2.52 μs	[append form 1 [2 3 4 5 6 7 8 9 10]]
== "12345678910"

;; native rejoin, interpreted
>> rejoin: func spec-of :rejoin body-of :rejoin
>> clock/times [rejoin [1 2 3 4 5 6 7 8 9 10]] 1e5
5.86 μs	[rejoin [1 2 3 4 5 6 7 8 9 10]]
== "12345678910"

;; your one
>> clock/times [rejoin [1 2 3 4 5 6 7 8 9 10]] 1e5
16.1 μs	[rejoin [1 2 3 4 5 6 7 8 9 10]]
== "12345678910"

@loziniak
Copy link
Author

loziniak commented Jan 3, 2024

At least it's not an order of magnitude. Perhaps it could be optimized, I did not write it with speed in mind.

@greggirwin
Copy link
Contributor

While I don't press for TDD most of the time, I do think that spec'ing expected results would be a good plan for this. So then we know what the edge cases and designed behavior are.

@greggirwin
Copy link
Contributor

This would be slower than now, but faster than @loziniak's.

rejoin-foreach: function [
    "Reduces and joins a block of values." 
    block [block!] "Values to reduce and join"
] [
    if empty? block: reduce block [return block]
    joined: either series? first block [copy first block] [to string! first block]
    foreach val next block [append joined val]
    joined
]

But we have to consider breaking code as well. In this case I can't guess how much depends on the current behavior, and feel fairly safe if we decide to change it. But this also falls into design chat on join/combine in this space.

@loziniak
Copy link
Author

spec'ing expected results would be a good plan for this

I'd start with examples from OP:

>> rejoin [[2 3][4 5]]
== [2 3 4 5]                    ; not: [2 3 [4 5]]
>> rejoin [quote (2 3) quote (4 5)]
== (2 3 4 5)                    ; not: (2 3 (4 5))
>> p: rejoin [quote a/b quote c/d]
== a/b/c/d
>> a: [b [c [d 10]]]
== [b [c [d 10]]]
>> get p
== 10                           ; not: none
>> to block! p
== [a b c d]                    ; not: [a b c/d]

@hiiamboris
Copy link

This seems nice and predictable but how would one make it output a string (primary use case)?

@greggirwin
Copy link
Contributor

greggirwin commented Feb 12, 2024

a: [b [c [d 10]]]

Same as now I suppose, by putting a string as the first value in the block. Or any non-block value for that matter.

@greggirwin
Copy link
Contributor

But we should look at this vs repend as well. As Boris notes, building strings is the primary use case, but I imagine a lot of us have code that builds blocks with it as well. Also handy for building paths, given a root path.

@greggirwin
Copy link
Contributor

Old join/combine idea: https://gist.github.com/greggirwin/24c022cb5dde5771531a8939309ccb43

I have an updated version with /only/case/exclude thoughts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants