# Left and right division for strings

For numbers and matrices, we have `x * y / y == x` and `x \ (x * y) == y`. Since we have `*` for strings, we could implement `/` and `\` for strings too:

``````"abcd" / "cd" == "ab"
"ab" \ "abcd" == "cd"
``````

AFAICT, it would be algebraically sound, useful, and fun. What do you think?

4 Likes

Very fun. What should the behaviour be if one does `"ab" \ "cd"`? The only reasonable thing seems to be to throw and error, which makes me wonder how much utility thisād really have.

That said, if we also supported division by a `Regex`, thatād actually make this thing quite a cute and occasionally useful pattern

5 Likes

Well, itās more like integer division though, i.e., you wonāt have

``````("abc" * "de") / "cde" == "abc" * ("de" / "cde")
``````
2 Likes

In what situation would this be used?

3 Likes

Python has `str.removeprefix` and `str.removesuffix` which are similar.

``````Signature: str.removeprefix(self, prefix, /)
Docstring:
Return a str with the given prefix string removed if present.

If the string starts with the prefix string, return string[len(prefix):].
Otherwise, return a copy of the original string.
``````
``````Signature: str.removesuffix(self, suffix, /)
Docstring:
Return a str with the given suffix string removed if present.

If the string ends with the suffix string and that suffix is not empty,
return string[:-len(suffix)]. Otherwise, return a copy of the original
string.
``````

Haskell has `stripPrefix` and `stripSuffix` for `List` (and String is a List of Char in Haskell).

Except that these donāt throw an error if the prefix/suffix arenāt present. A more natural analogue in Julia might be to add more optional arguments to `chop`, e.g. `chop(s, prefix="foo", suffix="bar")`. Julia already has the functions `chopprefix` and `chopsuffix` for this.

8 Likes

I donāt work with strings much, but I have found myself in a few situations before where Iāve done things like

``````if endswith(path, ".jl")
path[begin:end-3]
end
``````

``````if endswith(path, ".jl")
path / ".jl"
end
``````

Thatās a really simple example, but Iāve found a few times when writing string macros, that thereās often prefixes or suffixes I know are there that I want to chop off.

`chopsuffix(s, ".jl")` was added in Julia 1.8.

8 Likes

Sure, but whereās the fun in that?

2 Likes

Note also that this is buggy:

``````julia> s = "Ī±.jl"
"Ī±.jl"

julia> s[begin:end-3]
ERROR: StringIndexError: invalid index [2], valid nearby indices [1]=>'Ī±', [3]=>'.'
``````

(Itās really tempting to write this, which is a key motivation for a `StringIndex` type.)

8 Likes

Nice try, but I will not be tempted into that tar-pit of a thread today

8 Likes

Like the idea about regex and just checked that regular languages are closed under union, intersection, complement and concatenation. Thus one could indeed define difference as

``````\(ra::Regex, rb::Regex) = !rb ā© ra
``````

with `!` and `ā©` being complement and intersection respectively. Not sure if this efficient in general though?
Interestingly, `\` is not defined on sets as difference either ā the fallback method is applicable, but fails.

1 Like

Even though regular languages are closed under complementation, unfortunately I donāt think PCRE supports complement. How do I turn any regex into an complement of itself without complex hand editing? - Stack Overflow

Was proposed and declined in Julia#13411. Thereās also the consideration that `/` could alternatively represent path concatenation (also declined in Julia#9488).

8 Likes

I donāt like `Base./` for path concat, since it has nothing to do with division.

We should either stop using `*` for string concatenation or we should ādouble downā.

I agree with that. `append`/`concat` function Ā· Issue #53040 Ā· JuliaLang/julia Ā· GitHub proposes an alternative concatenation function `append`.

What do you expect `"de" / "cde"` to do here?

1 Like

Good question, by analogy with integer division it should be `zero`, i.e., an absorbing element for `*`.

1 Like

Just for fun:

``````import Base: /, \
/(x::String, y::String) = begin s = replace(x, y => "", count=1); s == x ? "" : s; end
\(x::String, y::String) = /(y::String, x::String)

"abcd" / "cd"       # == "ab"
"ab" \ "abcd"       # == "cd"
"de" / "cde"        # == ""
``````

Obviously it should be `"abdā»Ā¹cā»Ā¹"`

5 Likes

Or a `Rational{String}` typed `"ab"//"cd"`

7 Likes

Iād use `splitext` for this.

2 Likes