On clojure-jvm, you can’t implement your own String type or derive from String since java.lang.String is final. You could implement CharSequence though, and have that work as a pseudo string, since a lot of the “String” functions are actually built around CharSequence…
(defrecord mystring [line ^String contents]
java.lang.CharSequence
(charAt [this index]
(.charAt contents index))
(chars [this] (.chars contents))
(codePoints [this] (.codePoints contents))
(length [this] (.length contents))
(subSequence [this start end]
(.subSequence contents start end))
(toString [this] (.toString contents)))
user=> (def the-string (->mystring 22 "hello"))
user=> (require '[clojure.string :as s])
nil
user=> (s/includes? the-string "hel")
true
Another option is to just define your own string namespace with some protocol functionality to delegate to clojure.string, and then wrap the functions. Something like this:
(ns my.clojure.string
(:require [clojure.string :as s]))
(defprotocol IStringLike
(as-string ^String [this]))
(extend-protocol IStringLike
java.lang.String
(as-string [this] this)
java.lang.CharSequence
(as-string [this] (.toString this))
clojure.lang.IPersistentMap
(as-string [this] (this :text)))
(defn blank? ^String [s] (s/blank? (as-string s)))
(defn capitalize ^String [s] (s/capitalize (as-string s)))
(defn ends-with? [s substr] (s/ends-with? (as-string s) substr))
;;.... implement the other 10 functions
(def the-string "hello")
(def map-string {:line 23 :text "hello"})
(ends-with? the-string "o")
true
(ends-with? map-string "o")
true
(capitalize the-string)
"Hello"
(capitalize map-string)
"Hello"
could work. Another option is to define a protocol that implements the string operations defined in clojure.string, and use that for polymorphic operations. You could get custom behavior for stringlike types, e.g. lifting string operations into maps:
(defprotocol IString
(blank? [this])
(capitalize [this])
(ends-with? [this substr])
;;...define the other fns from clojure.string.
)
(extend-protocol IString
String
(blank? [this] (s/blank? this))
(capitalize [this] (s/capitalize this))
(ends-with? [this substr] (s/ends-with? this substr))
clojure.lang.IPersistentMap
(blank? [this] (s/blank? (this :text)))
(capitalize [this] (-> this (update :text s/capitalize)))
(ends-with? [this substr] (s/ends-with? (this :text) substr)))
my.clojure.string=> (capitalize map-string)
{:line 23, :text "Hello"}
my.clojure.string=> (capitalize the-string)
"Hello"