3
votes

I am using nix with reflex platform to compile haskell to javascript with GHCJS 8.0.1. I have written some functions that rely on an internal module in the text library: Data.Text.Internal. The only thing I need from this is the data constructor for Text:

data Text = Text
  {-# UNPACK #-} !A.Array -- payload (Word16 elements)
  {-# UNPACK #-} !Int     -- offset (units of Word16, not Char)
  {-# UNPACK #-} !Int     -- length (units of Word16, not Char)

However, when I try to compile code that uses this data constructor with GHCJS, it fails. The error message makes it clear that somehow, somewhere, the data constructor for Text has been replaced by:

newtype Text = Text JSString

I understand that I can put #ifdefs around blocks of code to work around this. However, I cannot figure out how this data constructor replacement has been accomplished. This is not mentioned anywhere in ghcjs-shims. It isn't mentioned is ghcjs either. Even weirder is that ghcjs-base has functions that use the normal data constructor for Text. I would appreciate if anyone could point me to the source of this replacement.

EDIT: This is not relevant to the question, but the reason I need to use the internals of Text is to efficiently encode things like IPv4 addresses and MAC addresses. In my the benchmarks I included with a previous question, I found that the fastest IPv4 encoding using the public API of text took around 450ns. Using the internals (and writing to a mutable array directly), I can do this in around 35ns, which is at least at 10x improvement in performance.

EDIT 2: Something else I just remembered (which is possibly relevant to the question) is that in another place in my code, I had used hexadecimal from Data.Text.Lazy.Builder.Int. However, with ghcjs, this function isn't exported by this module. I would be glad to contribute it upstream somewhere, but since I don't understand how all of the modules in text are getting shimmed out, I don't know where the appropriate upstream for this is. I would appreciate if someone could point me to the specific file where this is accomplished.

1
Can you go into details on why you need access to the internals of the Text representation? - Cactus
@Cactus I have added details about why I need to access the internals of Text. - Andrew Thaddeus Martin
The reason I'm asking, is because Data.Text.Internal should expose enough functionality to do what you want without looking at the representation itself. Your linked previous question doesn't seem to contain the Text-internals-using version so I can't tell for sure yet. - Cactus
I've looked through the other functions in Data.Text.Internal, and it looks like the text function does what I need in most situations. However, there are some other text builder abstractions I've been working on where I need something like this: textToMArray :: Text -> ST s (TA.MArray s). This copies a piece of text into a new buffer. I can write this when I have access to the data constructor, but I don't think that anything else in Data.Text.Internal lets me do this. I may be able to work around this though. Also, I'd still like to know where the data constructor switcharoo happens. - Andrew Thaddeus Martin
It would also be nice to see where the existing discussions of this are. I'm guessing that a longer-term goal would be to push the ghcjs-specific stuff into text itself (behind ifdefs). My cursory perusal of the github issue tracker turns up no talk of this, but I suspect that I just looking for information in the wrong place. - Andrew Thaddeus Martin

1 Answers

6
votes

It looks like you might be using reflex-platform. We recently integrated a change that uses JSString for Text rather than the usual implementation; that's because JSString is dramatically faster and uses a lot less memory than the pure-Haskell Text implementation.

If this is what you're running into, I'd encourage you to stick with JSString unless you're quite unconcerned about performance. However, if you'd like to disable this optimization, I think it should work to just comment out this block and then re-enter your try-reflex/work-on/nix-shell.