Using streams to clarify (?) the signature of Data.Text.replace

In the "text" package, the signature of Data.Text.replace http://hackage.haskell.org/package/text-1.2.2.1/docs/Data-Text.html#v:replac... always sends me looking into the haddocks: replace :: Text -> Text -> Text -> Text Which argument is the text to replace, which is the replacement and which is the text that should be scanned? Imagine a generalized version of replace that 1) works on streams, and 2) allows replacing a sequence of texts (like, say, chapter headers) instead of replacing the same text repeatedly. It could have the following signature: replace' :: Stream (Stream (Of Text) m) m ()
-> Stream (Stream (Of Text) m) m Void -> Stream (Of Text) m r -> Stream (Of Text) m r
Do you find easy to intuit, just by looking at that signature, which is the function of each argument?

(Apologies if you receive this twice, but it seems my previous attempt
was sent to a non-existent Google Group rather than Cafe :s)
On 27 June 2016 at 00:39, Daniel Díaz
In the "text" package, the signature of Data.Text.replace always sends me looking into the haddocks:
replace :: Text -> Text -> Text -> Text
Which argument is the text to replace, which is the replacement and which is the text that should be scanned?
Imagine a generalized version of replace that 1) works on streams, and 2) allows replacing a sequence of texts (like, say, chapter headers) instead of replacing the same text repeatedly. It could have the following signature:
replace' :: Stream (Stream (Of Text) m) m () -> Stream (Stream (Of Text) m) m Void -> Stream (Of Text) m r -> Stream (Of Text) m r
Do you find easy to intuit, just by looking at that signature, which is the function of each argument?
Whilst this type signature is easier to intuit which is the actual text in which the replacement is occurring, it gives me no clues as to which of the first two arguments is the text to search for and which is the replacement. -- Ivan Lazar Miljenovic Ivan.Miljenovic@gmail.com http://IvanMiljenovic.wordpress.com

On 27/06/16 2:39 AM, Daniel Díaz wrote:
In the "text" package, the signature of Data.Text.replace http://hackage.haskell.org/package/text-1.2.2.1/docs/Data-Text.html#v:replac... always sends me looking into the haddocks:
replace :: Text-> Text -> Text-> Text
Which argument is the text to replace, which is the replacement and which is the text that should be scanned?
Considering partial application, the order that makes the most sense is - what to look for - what to replace it with - the big string to search and replace in I've never used this function. Let's go look at the documentation. Pats self on back: nailed it.
Imagine a generalized version of replace that 1) works on streams, and 2) allows replacing a sequence of texts (like, say, chapter headers) instead of replacing the same text repeatedly. It could have the following signature:
replace' :: Stream (Stream (Of Text) m) m () -> Stream (Stream (Of Text) m) m Void -> Stream (Of Text) m r -> Stream (Of Text) m r
Do you find easy to intuit, just by looking at that signature, which is the function of each argument?
Absolutely not. In fact, this crossed my personal complexity horizon and is still accelerating towards some kind of singularity. The more I try to imagine a problem that this might be a solution to, the less I can understand why it would be approached this way. To be perfectly honest, I've never found replacing one string by another to be terribly useful. Replacing one *token sequence* by another, yes. Replacing a (match for a) regular expression by a derived string, yes.

(I mistakenly posted this thread in Haskell-cafe instead of the Haskell Pipes list. Oh well.) On Monday, June 27, 2016 at 2:57:15 AM UTC+2, Richard A. O'Keefe wrote:
replace :: Text-> Text -> Text-> Text
Which argument is the text to replace, which is the replacement and which is the text that should be scanned?
Considering partial application, the order that makes the most sense is - what to look for - what to replace it with - the big string to search and replace in
Interesting, I forgot that the order of parameters can tell you things about how they are used. Did you arrive at your conclusion by assuming the "most variable" arguments come last?
Imagine a generalized version of replace that 1) works on streams, and
2) allows replacing a sequence of texts (like, say, chapter headers) instead of replacing the same text repeatedly. It could have the following signature:
replace' :: Stream (Stream (Of Text) m) m () -> Stream (Stream (Of Text) m) m Void -> Stream (Of Text) m r -> Stream (Of Text) m r
Do you find easy to intuit, just by looking at that signature, which is the function of each argument?
Absolutely not. In fact, this crossed my personal complexity horizon and is still accelerating towards some kind of singularity.
Yeah, I was mostly experimenting about how to encode the the purpose of each argument in the types, without giving much thought to how complicated the signature ended up being. About the first two function arguments, my reasoning was: the first stream (that returns ()) can be finite or infinite while the second (that returns Void) is necessarily infinite. Therefore it makes sense that the first is the stream of things to search for, and the second the stream of substitutions, that will be consumed as long as matches are found.
The more I try to imagine a problem that this might be a solution to, the less I can understand why it would be approached this way.
A possible use case: you want to "splice" the contents of a sequence of files into another file at certain words, without having to keep whole files in memory, and without using lazy I/O.

> Imagine a generalized version of replace that 1) works on streams, and
> replace' :: Stream (Stream (Of Text) m) m () > -> Stream (Stream (Of Text) m) m Void > -> Stream (Of Text) m r > -> Stream (Of Text) m r > > > Do you find easy to intuit, just by looking at that signature, which > is the function of each argument?
Absolutely not. In fact, this crossed my personal complexity horizon and is still accelerating towards some kind of singularity.
I agree 100%. What is going on here? If you just wanted to clarify the signature, why not just use type synonyms? type Replaced = Text type Replacement = Text replace :: Replaced -> Replacement -> Text -> Text No breaking changes necessary. You don't even have to export the synonyms. Or generalize the function in a much simpler fashion: replace :: (Text -> Text) -> Text -> Text There should be no ambiguity here. It's less powerful than the stream-monster because the argument function is stateless - but then whatever kind of stream you use probably has some zipWith function that you could use for more complicated cases.
A possible use case: you want to "splice" the contents of a sequence of files into another file at certain words, without having to keep whole files in memory, and without using lazy I/O.
I'm not really familiar with streams or pipes, but that does sound like a perfect use case for conduits. Still, I'm at a loss how that thing would look internally. How do you "find" a stream of Text in another Text? What does that even mean, semantically? So if I understand the use case correctly, you have some "trigger words" in a Text and want to replace each trigger with the contents of a file, as in a templating system. The simplest type I could come up with for that would be replace' :: Map Text (Stream (Of Text) m ()) -> Stream (Of Text) m r -> Stream (Of Text) m r Note: No Stream on the left side of an arrow. No Stream of triggers, so ordering is not important anymore. It wasn't helpful anyway. Easily generalized to non-text stuff. And as such it may already be part of some streaming libraries. Which is exactly as it should be: streaming functions should be part of streaming libraries, not a text library.

Which is exactly as it should be: streaming functions should be part of streaming libraries, not a text library.
_______________________________________________
Note however that a stream of strict texts is like a generalized Data.Text.Lazy.Text in which you can have effects when producing each chunk, and there's also a result value at the end that is useful for things like dividing the stream. Text-specific functions that worked on such streams (say, divide the stream at a given Char) would belong in a text library, I think. The package streaming-bytestring http://hackage.haskell.org/package/streaming-bytestring does something similar for lazy bytestrings.

On 27/06/16 7:17 PM, Daniel Díaz wrote:
Considering partial application, the order that makes the most sense is
- what to look for - what to replace it with - the big string to search and replace in
Interesting, I forgot that the order of parameters can tell you things about how they are used. Did you arrive at your conclusion by assuming the "most variable" arguments come last?
No, by considering "what would be most useful as a specialisation". It's different psychologically, but not, I believe, pragmatically. Of course, that approach only worked this time because whoever designed the function must have done some similar thinking.
participants (4)
-
Daniel Díaz
-
Ivan Lazar Miljenovic
-
MarLinn
-
Richard A. O'Keefe