@@ -14,6 +14,7 @@ module Data.ArrayBuffer.Builder
17, putArrayBuffer
18, putArrayBuffer
@@ -32,7 +33,9 @@ module Data.ArrayBuffer.Builder
34, execBuilder
35, execBuilder
35, singleton
37, singleton
37, encodeUint8
40, encodeUint8
38, encodeInt8
41, encodeInt8
@@ -53,7 +56,7 @@ where
54import Prelude
57import Prelude
56−import Control.Monad.Writer.Trans (WriterT, execWriterT, tell)
59+import Control.Monad.Writer.Trans (WriterT, execWriterT, tell, lift)
57import Data.ArrayBuffer.ArrayBuffer as AB
60import Data.ArrayBuffer.ArrayBuffer as AB
58import Data.ArrayBuffer.DataView as DV
61import Data.ArrayBuffer.DataView as DV
59import Data.ArrayBuffer.Typed as AT
62import Data.ArrayBuffer.Typed as AT
@@ -67,10 +70,12 @@ import Effect.Class (class MonadEffect, liftEffect)
67-- | The `PutM` monad is a `WriterT Builder` transformer monad which
70-- | The `PutM` monad is a `WriterT Builder` transformer monad which
68-- | gives us do-notation for the `Builder` monoid. The base monad must be
71-- | gives us do-notation for the `Builder` monoid. The base monad must be
69-- | a `MonadEffect`.
72-- | a `MonadEffect`.
74+-- | To append `Builder`s in this monad call `tell`, or any of the `put*`
75+-- | functions in this module.
70type PutM = WriterT Builder
76type PutM = WriterT Builder
72−-- | The `Put` monad is a `WriterT Builder Effect` monad which
78+-- | The `PutM` type reified to `Effect`, in other words `WriterT Builder Effect`.
73−-- | gives us do-notation for the `Builder` monoid.
74type Put = PutM Effect
79type Put = PutM Effect
76-- | Monoidal builder for `ArrayBuffer`s.
81-- | Monoidal builder for `ArrayBuffer`s.
@@ -143,10 +148,18 @@ foldM f a0 = foldl (\ma b -> ma >>= flip f b) (pure a0)
143singleton :: ArrayBuffer -> Builder
148singleton :: ArrayBuffer -> Builder
144singleton buf = Node Null buf Null
149singleton buf = Node Null buf Null
151+-- | Prepend an `ArrayBuffer` to the beginning of the `Builder`. *O(1)*
152+cons :: ArrayBuffer -> Builder -> Builder
153+cons x bs = Node Null x bs
146−-- | Add an `ArrayBuffer` to the end of the `Builder`. *O(1)*
155+-- | Append an `ArrayBuffer` to the end of the `Builder`. *O(1)*
147snoc :: Builder -> ArrayBuffer -> Builder
156snoc :: Builder -> ArrayBuffer -> Builder
148snoc bs x = Node bs x Null
157snoc bs x = Node bs x Null
159+-- | Calculate the total byte length of the `Builder`. *O(n)*
160+length :: Builder -> Int
161+length bldr = foldl (\b a -> b + AB.byteLength a) 0 bldr
150-- | Build a single `ArrayBuffer` from a `Builder`. *O(n)*
163-- | Build a single `ArrayBuffer` from a `Builder`. *O(n)*
151execBuilder :: forall m. (MonadEffect m) => Builder -> m ArrayBuffer
164execBuilder :: forall m. (MonadEffect m) => Builder -> m ArrayBuffer
152execBuilder bldr = do
165execBuilder bldr = do
@@ -173,6 +186,28 @@ execPutM = execBuilder <=< execWriterT
173execPut :: Put Unit -> Effect ArrayBuffer
186execPut :: Put Unit -> Effect ArrayBuffer
174execPut = execPutM
187execPut = execPutM
189+-- | Build up a sub-`Builder` without `tell`ing it to the `Writer` yet.
191+-- | One case where we might want to call a `subBuilder` is when
192+-- | serializing length-prefixed messages in some protocol. In that case,
193+-- | we must serialize the message first, calculate the message length,
194+-- | append the message length, and then append the message.
196+-- | In a `PutM` monad do-block, we can
201+-- | messageBuilder <- subBuilder $ do
205+-- | putInt32 $ length messageBuilder
206+-- | tell messageBuilder
208+subBuilder :: forall m. (MonadEffect m) => PutM m Unit -> PutM m Builder
209+subBuilder = lift <<< execWriterT
176-- | Append an `ArrayBuffer` to the builder.
211-- | Append an `ArrayBuffer` to the builder.
177putArrayBuffer :: forall m. (MonadEffect m) => ArrayBuffer -> PutM m Unit
212putArrayBuffer :: forall m. (MonadEffect m) => ArrayBuffer -> PutM m Unit
178putArrayBuffer = tell <<< singleton
213putArrayBuffer = tell <<< singleton
@@ -404,4 +439,5 @@ putFloat64le = putArrayBuffer <=< encodeFloat64le
404-- so that if we want to change the Builder implementation later then we can.
439-- so that if we want to change the Builder implementation later then we can.
406-- Another way this could go:
441-- Another way this could go:
407−-- https://kodimensional.dev/posts/2019-03-25-comonadic-builders
442+-- https://kodimensional.dev/posts/2019-03-25-comonadic-builders