Skip to content

Conversation

felixSchl
Copy link
Contributor

Sill WIP, but feedback welcome.

@felixSchl
Copy link
Contributor Author

Are you guys open to using purescript-spec for the tests? I think we can improve the coverage and readability a lot by going down that route.

exports.onReadable = function(s) {
return function(f) {
return function() {
s.on('readable', function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just s.on('readable', f) should work here, right?

@hdgarrood
Copy link
Member

Sorry for the nitpicks, this looks good 👍 just checking, this doesn't break anything, right?

I'd rather not depend on purescript-spec, as if it or anything it depends on later decides to depend on this package, it turns into a bit of a mess, unfortunately.

data Chunk
data Data = BufferData Buffer
| StringData String
| UnknownData Chunk
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using Foreign here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of defining a new type Chunk, I mean.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind, but it might make the code a bit harder to read because of the ambiguity of Foreign.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My impression is that object mode lets you put any javascript value through a stream (apart from null which has a special meaning for the end of the stream). Given this, I think Foreign is appropriate; that is what the type represents, and you also get plenty of useful functions for turning Foreign values into data types that you can do stuff with.

Why do you say Foreign is more ambiguous than your Chunk?

Looking at the node API again though, if we were going to support object mode, I think I would rather have an entirely separate data type for streams in object mode anyway, especially since no Node APIs use them. I don't want to have to worry about whether a stream is in object mode when it ought to be possible to rule that out at compile time.

@hdgarrood
Copy link
Member

What about it is WIP?

@felixSchl
Copy link
Contributor Author

Thank you for your valuable feedback. I checked it in before heading to work today so I knew there would be some rough edges (like e.g. forgetting to match Nothing), hence the WIP. I also think it's still WIP because the tests are not scrutinizing enough (I got away with a obviously missing pattern).

@hdgarrood
Copy link
Member

Cool :)

Also, do you have a use case for the UnknownData constructor? It seems like the only case where we would need it would be if the stream was in object mode, which I think I'd rather not try to support until we have a real use case for that (just because, in my experience, trying to do bindings without a use case doesn't work very well). How about throwing an error in the case where the data is neither a String nor a Buffer?

@felixSchl
Copy link
Contributor Author

Yeah I was thinking of objectMode, but I agree with what you are saying about guessing usecases.

@felixSchl
Copy link
Contributor Author

Alright, I forced pushed my fixes. I have made all changes as per review and added another test. Lucky I did, it caught another problem. If you are happy with this version, I am happy to take away the "WIP" from the title and have it merged.

@felixSchl
Copy link
Contributor Author

Actually now that we disregard object mode, we are back to two constructors, and could use Either instead of Data again.

@hdgarrood
Copy link
Member

Great :)

I was just thinking the same thing about Data vs Either, yeah. I'd rather stick to one or the other; there doesn't seem to be much point in having a Data type if it's isomorphic to Either String Buffer and if we're going to only use Either String Buffer in the exported API. I don't feel strongly, but I think I'd vote for sticking to Either everywhere, just to avoid breakage.

@felixSchl
Copy link
Contributor Author

I agree. I removed the ADT and used a type alias.

@felixSchl
Copy link
Contributor Author

I see a problem though between onDataString and readString. onDataString takes a encoding parameter. Should readString do, too? Or should there be two variants?

return function(f) {
return function() {
r.on('data', function(data) {
f(readChunk(data));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is missing a () at the end of the line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch... bummer the tests did not pick up on it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did the tests catch this? If not, could you add one please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, actually not sure how to test this, since we are not using Aff or ContT. In other words, if no callback is called, Eff don't care.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good point. Ok, let's not worry then.

@hdgarrood
Copy link
Member

I'd like read to follow the same pattern as onData, ideally, yeah. My reasoning was that this library should discourage calling setEncoding on the stream, because doing so loses information and irreversibly changes the behaviour of all streaming functions, just for the sake of a minor convenience. Therefore it tries to be most comfortable when setEncoding has not been called. onData is for if you want a Buffer, and onDataString is for if you want a String (and because the stream is actually producing Buffer values, you need an encoding to get String values out). onDataEither is for the (hopefully very rare) case where you have a stream which might have had setEncoding called on it, and you need to handle that. I think ideally we'd make read and readString follow the same pattern, if that makes sense.

onDataEither :: forall r eff. Readable r (err :: EXCEPTION | eff) -> (Either String Buffer -> Eff (err :: EXCEPTION | eff) Unit) -> Eff (err :: EXCEPTION | eff) Unit
onDataEither r cb = onData' r cb

-- | Listen for `data` events, returning
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just inline this definition into onDataEither now?

@felixSchl
Copy link
Contributor Author

Alright so readString takes an Encoding now and throws if it reads a string from the buffer because that would imply setEncoding was called, just like onDataString. I have also inlined onDataEither.

@felixSchl felixSchl changed the title [WIP] Expose read and on('readable') Expose read and on('readable') May 20, 2016
-- | A duplex (readable _and_ writable stream)
type Duplex = Stream (read :: Read, write :: Write)

type Data = Either String Buffer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this type synonym (and also ChunkReader) give us over writing out the real types? I generally tend to avoid type synonyms like these, as then there's more to keep in your head when you're reading the code.

case v of
Nothing -> pure Nothing
Just (Left _) -> throw "Stream encoding should not be set"
Just (Right buf) -> pure <$> (unsafeInterleaveEff $ Buffer.toString enc buf)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would you feel about using Just rather than pure here?

@felixSchl
Copy link
Contributor Author

Alright, I'll do proper commits now until we're done. Then I'll squash and then we can merge

@felixSchl
Copy link
Contributor Author

Reason being it's easier for us to review how we got here.

@hdgarrood
Copy link
Member

Ok, absolute last thing and then we're done - the comment on readChunk refers to the Data type which no longer exists. You could update it or remove it, up to you.

@hdgarrood
Copy link
Member

Also, if you are going to just update it, I think it ought to go on the function itself, rather than the Chunk data type (as it currently is)

@felixSchl
Copy link
Contributor Author

Sweet, I just removed it. It's also squashed.

@hdgarrood
Copy link
Member

Looks great, thanks very much! Sorry this was such an ordeal, it's just that it's quite high up in the dependency tree of purescript-node packages so I think it pays to be extra careful. 🎉

@hdgarrood hdgarrood merged commit 8b9a6d5 into purescript-node:master May 20, 2016
@felixSchl
Copy link
Contributor Author

No worries, I enjoyed working through this and learned a thing or two.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants