# Why doesn't islowercase work on String?

**URL:** <https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173>\
**Category:** General Usage\
**Tags:** strings\
**Created:** [August 23, 2022, 5:48am UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173 "2022-08-23T05:48:57Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [August 23, 2022, 5:48am UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/1 "2022-08-23T05:48:57Z")

</div>

Why doesn’t `islowercase` work on String? It only works on characters.

---

<div class="post-metadata">

**Author:** ![RobertGregg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/robertgregg/32/22105_2.png) [@RobertGregg](https://discourse.julialang.org/u/RobertGregg)\
**Post date:** [August 23, 2022, 5:58am UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/2 "2022-08-23T05:58:31Z")

</div>

Hmm that is a little unexpected. I guess its because a String could have a mix of upper and lowercase characters so you have to check all the characters like this:

```julia
julia> all(x->islowercase(x), "hello")
true

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [August 23, 2022, 5:59am UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/3 "2022-08-23T05:59:06Z")

</div>

conceptually it’s like why does `iseven` only work on number not array,

> [@RobertGregg](#):
>
> ```julia
> julia> all(x->islowercase(x), "hello")
> true
> 
> ```

you can simply write

```julia
all(islowercase, "hello")

```

---

<div class="post-metadata">

**Author:** ![RobertGregg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/robertgregg/32/22105_2.png) [@RobertGregg](https://discourse.julialang.org/u/RobertGregg)\
**Post date:** [August 23, 2022, 6:04am UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/4 "2022-08-23T06:04:11Z")

</div>

oops, I’m used to writing more complicated conditionals that I default to anonymous functions, good catch.

---

<div class="post-metadata">

**Author:** ![digital\_carver](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/digital_carver/32/33818_2.png) [@digital\_carver](https://discourse.julialang.org/u/digital_carver)\
**Post date:** [August 23, 2022, 7:05am UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/5 "2022-08-23T07:05:14Z")

</div>

Let’s say the string is `"Hello"`. Most of that string is in lowercase, but one letter isn’t. So there’s no binary answer to the question `islowercase("Hello")`.

Intuitively, what we’re thinking of when we ask `islowercase("Hello")` is `is_all_lowercase("Hello")`. Since `all` already exists, it makes sense to do `all(islowercase, "Hello")` instead, since it composes existing functionality and isn’t any more complicated or longer that a special function would be.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [August 23, 2022, 8:31am UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/6 "2022-08-23T08:31:11Z")

</div>

I think `lowercase` working on `String` would be reasonable, and that its meaning is clear. Not least because the function `lowercase` _does_ work on strings:

```julia
julia> lowercase("HelLo")
"hello"

```

It is a bit odd that the above works, but `islowercase(lowercase("HelLo"))` errors.

---

<div class="post-metadata">

**Author:** ![ptoche](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ptoche/32/23554_2.png) [@ptoche](https://discourse.julialang.org/u/ptoche)\
**Post date:** [August 24, 2022, 12:13am UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/7 "2022-08-24T00:13:28Z")

</div>

`lowercase("HelLo")` is not ambiguous, while `islowercase("HelLo")` is a little. It would be reasonable to expect it to return something like `(false,true,true,false,true)`.

Once you’ve seen it, the following syntax is quite natural:

```julia
julia> all(islowercase, "Hello")
false

julia> any(islowercase, "Hello")
true

```

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [August 24, 2022, 4:57am UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/8 "2022-08-24T04:57:57Z")

</div>

> [@ptoche](#):
>
> It would be reasonable to expect it to return something like `(false,true,true,false,true)`.

I disagree, that would be very surprising, and something one might expect from `islowercase.()`. The `issomething` functions _always_ return a scalar `Bool`.

IMO, `lowercase(str)` and `islowercase(str)` seem like a natural pair, while `all(islowercase, str)` seems to be more naturally paired with `map(lowercase, str)`, in that both functions would need mapping over a string.

It also corresponds to how we talk about it in language, there is no ambiguity about the concepts ‘a lowercase string’, ‘an uppercase string’, and ‘a mixed-case string’.

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [August 30, 2022, 6:19pm UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/9 "2022-08-30T18:19:24Z")

</div>

Issue here

> <https://github.com/JuliaLang/julia/issues/46444>
>
> In 1.7 \`islowercase\` is defined for characters but not strings. It would be usef…ul to have for strings.
> 
> (\[From discourse\](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173).)

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [August 30, 2022, 6:54pm UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/10 "2022-08-30T18:54:38Z")

</div>

Would such a definition have any contraindications?

```julia
julia> islowercase("Hello")
ERROR: MethodError: no method matching islowercase(::String)
Closest candidates are:
  islowercase(::AbstractChar) at strings/unicode.jl:324
Stacktrace:
 [1] top-level scope
   @ c:\Users\sprmn\.julia\v1.8\string2.jl:19

julia> import Unicode.islowercase

julia> islowercase(s::String) = s==lowercase(s)
islowercase (generic function with 2 methods)

julia> islowercase("Hello")
false

```

---

<div class="post-metadata">

**Author:** ![sylvaticus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sylvaticus/32/203883_2.png) [@sylvaticus](https://discourse.julialang.org/u/sylvaticus)\
**Post date:** [August 30, 2022, 7:17pm UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/11 "2022-08-30T19:17:04Z")

</div>

That issue is now closed.

I disagree with what feel natural, so I am gonna fork Julia, rebrand it `Julie` and implement `lowercase(::String)` 😁

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [August 30, 2022, 7:40pm UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/12 "2022-08-30T19:40:22Z")

</div>

What definition do you want? Consider:

```julia
julia> all(islowercase, "élan")
false

julia> all(islowercase, "élan")
true

```

(Hint: run `collect` on these two strings.)

> [@rocco\_sprmnt21](#):
>
> Would such a definition have any contraindications? […] `islowercase(s::String) = s==lowercase(s)`
> 
> ```julia
> 
> ```

Consider:

```julia
julia> Base.islowercase(s::AbstractString) = s==lowercase(s)

julia> islowercase("1")
true

julia> islowercase('1')
false

```

which seems inconsistent.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [August 31, 2022, 9:21am UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/13 "2022-08-31T09:21:35Z")

</div>

I see.  
But this inconsistency seems to derive (also) from the fact that the following functions that are used in the definition of `lowercase()` and `islowercase()`, give these results:

```julia
julia> c2l=Char(ccall(:utf8proc_tolower, UInt32, (UInt32,), '1'))
'1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)

julia> Bool(ccall(:utf8proc_islower, Cint, (UInt32,), UInt32(c2l)))
false

```

If I don’t make logical mistakes: we have a function that transforms a character into its lowercase form and a function that checking this result says it is not lowercase.

I understood, reading here and there that these topics depend on many “variables” that it is not easy to keep together in a simple way.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [August 31, 2022, 2:53pm UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/14 "2022-08-31T14:53:51Z")

</div>

> [@rocco\_sprmnt21](#):
>
> If I don’t make logical mistakes: we have a function that transforms a character into its lowercase form and a function that checking this result says it is not lowercase.

As far as I can tell, _every_ widely available string library in _every_ mainstream language does this: there is a `lowercase`-like function which converts characters to lowercase _if possible_ (and otherwise leaves them alone), and an `islower`-like function that checks specificaly whether a character is a _lowercase letter_.

(The `islower` predicate stems originally from the function of the same name in the [C standard library](https://www.mkssoftware.com/docs/man3/islower.3.asp).)

For example, in Python 3 (which doesn’t have a distinction between string and character types):

```py
>>> "1".lower()
'1'

>>> "1".islower()
False

```

See also the Ruby [`downcase`](https://apidock.com/ruby/String/downcase) — Ruby doesn’t provide an `islower` predicate, and instead the standard recommendation seems to be to [write a regex](https://stackoverflow.com/questions/12713251/ruby-how-to-tell-if-character-is-upper-lowercase). Or Swift’s [`string.lowercased()`](https://developer.apple.com/documentation/swift/string/lowercased()) method and [`char.isLowercased`](https://developer.apple.com/documentation/swift/character/islowercase) property. Or the C# [`String.ToLower()`](https://docs.microsoft.com/en-us/dotnet/api/system.string.tolower?view=net-6.0) and [`Char.IsLower`](https://docs.microsoft.com/en-us/dotnet/api/system.char.islower?view=net-6.0) methods. Or the Go [`ToLower(str)`](https://pkg.go.dev/strings#ToLower) and [`IsLower(char)`](https://pkg.go.dev/unicode#IsLower) functions. Or …

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [August 31, 2022, 3:51pm UTC](https://discourse.julialang.org/t/why-doesnt-islowercase-work-on-string/86173/15 "2022-08-31T15:51:14Z")

</div>

Thanks for taking the time to clear up all of these things.  
I had no doubt that the choice was unfounded, even without the fact that all other languages have made the same choice for these two functions.  
I still have some curiosities about why.  
To simplify, let’s imagine that at some point, in the definition of the lowercase () function, it was decided, for many good reasons, that for characters that do not have the upper-lower correspondent to leave them as they are rather than raise an error or other alternative.  
But leaving them as they are could be done with two opposite choices: one that interprets the characters as both upper and lower (this would not have the out of tune I pointed out, but who knows how many other problems it would bring with it); the other (the one actually taken) to consider these characters neither upper nor lower.
