Closed davidchambers closed 4 years ago
I did like that with the other PR both functions began with the regexp, but with these names the parameter order you have makes perfect sense and reads well.
It is convenient for S.replaceBy
to take the replacement function first (and thus the pattern second) in case one wishes to define a variant for patterns without optional capturing groups:
// replace__ :: RegExp -> (Array String -> String) -> String -> String
//
// Variant of proposed function `S.replace_` (sanctuary-js/sanctuary#685)
// for patterns without optional capturing groups.
const replace__ = S.flip (S.compose (S.flip (S.replace_)) (S.contramap (S.justs)));
// replaceBy_ :: (Array String -> String) -> RegExp -> String -> String
//
// Variant of proposed function `S.replaceBy` (sanctuary-js/sanctuary#686)
// for patterns without optional capturing groups.
const replaceBy_ = S.compose (S.replaceBy) (S.contramap (S.justs));
I am aware of two other reasons to take the pattern second:
replaceWith
and replaceBy
feel more closely related if their types are different → same → same → same rather than same → different → same → same; andreplaceWith ('-')
mirrors joinWith ('-')
.Superseded by #693
Competes with #685
This pull request adds the following two functions:
Design decisions:
Both functions take
pattern :: RegExp
, so there is no straightforward equivalent ofs1.replace (s2, s3)
for stringss1
,s2
, ands3
. One could useS.replaceWith (s3) (S.regex ('') (S.regexEscape (s2))) (s1)
, but it would be simpler and clearer to useString#replace
directly in such cases.Using
pattern :: RegExp
exclusively provides two benefits:Simplicity. Supporting both
RegExp
andString
would require splittingreplaceWith
into two functions (which would pose a naming challenge) or usingEither RegExp String
(which would not be ergonomic).Clarity. It's unclear whether
R.replace ('o') ('x') ('foo')
should evaluate to'fxo'
or to'fxx'
, whereas the presence of theg
flag in/o/g
indicates that all occurrences ofo
should be replaced, and the absence of theg
flag in/o/
indicates that only the first occurrence ofo
should be replaced.Unlike
replace
,S.replaceWith
does not support special replacement patterns. We are not obligated to expose every feature of the underlying API, and withS.replaceBy
it is possible to reference values matched by capturing groups (the primary use case for special replacement patterns).Like
replace'
,S.replaceBy
ignores the “offset
” and “string
” arguments provided byString#replace
(when given a function). These are rarely useful.Unlike
replace'
,S.replaceBy
ignores the “groups
” argument provided byString#replace
(when given a function) if the pattern contains any named capturing groups. I included a fix for this deficiency in purescript/purescript-strings#126.Unlike
replace'
,S.replaceBy
ignores the “match
” argument provided byString#replace
(when given a function). This simplifies the function's type, and saves one from usingS.K (...)
when only the captured groups are important (as is commonly the case). If one does require access to the matched substring as a whole, one can use/(...)/
to capture it. Admittedly, this approach is impractical if the RegExp object is defined in another module, but in such exceptional cases one can of course useString#replace
directly.Unlike
replace'
,S.replaceBy
acknowledges the existence of optional capturing groups:Evaluating the equivalent PureScript expression results in an exception being thrown:
I have proposed the use of
Array (Maybe String)
in place ofArray String
in purescript/purescript-strings#126.I have opened sanctuary-js/sanctuary-site#88 to provide several examples of
replaceWith
andreplaceBy
in use.@sanctuary-js/owners, what do you think of the proposed additions to the library?