Skip to content

Commit

Permalink
Fix type inference by exposing classless API (#53)
Browse files Browse the repository at this point in the history
* Drop unused import

* Fix type inference via classless API
  • Loading branch information
JordanMartinez committed Jul 1, 2023
1 parent 4ec460a commit f481921
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 51 deletions.
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,43 @@ Notable changes to this project are documented in this file. The format is based
## [Unreleased]

Breaking changes:
- Expose Buffer API using typeclass-less API witout removing typeclass API (#53 by @JordanMartinez)

Previously, compiler would fail to infer the type of `Buffer.create 1` as `Effect Buffer`
because the Buffer API was exposed only via the multiparameter typeclass `MonadBuffer`.
Due to the functional dependency between the two parameters, the monadic type cannot be inferred
until the buffer type is known (either `Buffer` or `STBuffer`).:
```purs
import Node.Buffer as Buffer
-- Example 1
main :: Effect Unit
main = do
x <- Buffer.create 1 -- compiler: is this `Int -> Effect Buffer` or `Int -> ST h (STBuffer h)?
pure unit
```

The workaround was to add a type annotation, indicating the `x` is a `Buffer`:
```purs
import Node.Buffer as Buffer
-- Example 2
main :: Effect Unit
main = do
x :: Buffer <- Buffer.create 1 -- compiler: Oh! It's `Int -> Effect Buffer`
pure unit
```

This change does not break anyone's code if one was using a `create` (or another such typeclass member)
to get `Int -> Effect Buffer`. Rather, such users can now drop the `:: Buffer` annotation
(i.e. Example 1 above now compiles).

If one was using `create` to get `forall m buf. MonadBuffer buf m => Int -> m buf`,
then one will need to update their imports:
```diff
-import Node.Buffer (class MonadBuffer)
+import Node.Buffer.Class (class MonadBuffer)
```

New features:

Expand Down
147 changes: 122 additions & 25 deletions src/Node/Buffer.purs
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,136 @@
module Node.Buffer
( Buffer
, module TypesExports
, module Class
, create
, freeze
, unsafeFreeze
, thaw
, unsafeThaw
, fromArray
, fromString
, fromArrayBuffer
, toArrayBuffer
, read
, readString
, toString
, write
, writeString
, toArray
, getAtOffset
, setAtOffset
, slice
, size
, concat
, concat'
, copy
, fill
) where

import Prelude

import Data.ArrayBuffer.Types (ArrayBuffer)
import Data.Maybe (Maybe)
import Effect (Effect)
import Node.Buffer.Class (class MutableBuffer)
import Node.Buffer.Class (class MutableBuffer, concat, concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, fromString, getAtOffset, read, readString, setAtOffset, size, slice, thaw, toArray, toArrayBuffer, toString, unsafeFreeze, unsafeThaw, write, writeString) as Class
import Node.Buffer.Immutable (ImmutableBuffer)
import Node.Buffer.Internal as Internal
import Node.Buffer.Types (BufferValueType(..), Octet, Offset) as TypesExports
import Node.Buffer.Types (BufferValueType)
import Node.Encoding (Encoding)

-- | A reference to a mutable buffer for use with `Effect`
foreign import data Buffer :: Type

instance mutableBufferEffect :: MutableBuffer Buffer Effect where
create = Internal.create
freeze = Internal.copyAll
unsafeFreeze = Internal.unsafeFreeze
thaw = Internal.copyAll
unsafeThaw = Internal.unsafeThaw
fromArray = Internal.fromArray
fromString = Internal.fromString
fromArrayBuffer = Internal.fromArrayBuffer
toArrayBuffer = Internal.toArrayBuffer
read = Internal.read
readString = Internal.readString
toString = Internal.toString
write = Internal.write
writeString = Internal.writeString
toArray = Internal.toArray
getAtOffset = Internal.getAtOffset
setAtOffset = Internal.setAtOffset
slice = Internal.slice
size = Internal.size
concat = Internal.concat
concat' = Internal.concat'
copy = Internal.copy
fill = Internal.fill
create = create
freeze = freeze
unsafeFreeze = unsafeFreeze
thaw = thaw
unsafeThaw = unsafeThaw
fromArray = fromArray
fromString = fromString
fromArrayBuffer = fromArrayBuffer
toArrayBuffer = toArrayBuffer
read = read
readString = readString
toString = toString
write = write
writeString = writeString
toArray = toArray
getAtOffset = getAtOffset
setAtOffset = setAtOffset
slice = slice
size = size
concat = concat
concat' = concat'
copy = copy
fill = fill

create :: Int -> Effect Buffer
create = Internal.create

freeze :: Buffer -> Effect ImmutableBuffer
freeze = Internal.copyAll

unsafeFreeze :: Buffer -> Effect ImmutableBuffer
unsafeFreeze = Internal.unsafeFreeze

thaw :: ImmutableBuffer -> Effect Buffer
thaw = Internal.copyAll

unsafeThaw :: ImmutableBuffer -> Effect Buffer
unsafeThaw = Internal.unsafeThaw

fromArray :: Array Int -> Effect Buffer
fromArray = Internal.fromArray

fromString :: String -> Encoding -> Effect Buffer
fromString = Internal.fromString

fromArrayBuffer :: ArrayBuffer -> Effect Buffer
fromArrayBuffer = Internal.fromArrayBuffer

toArrayBuffer :: Buffer -> Effect ArrayBuffer
toArrayBuffer = Internal.toArrayBuffer

read :: BufferValueType -> Int -> Buffer -> Effect Number
read = Internal.read

readString :: Encoding -> Int -> Int -> Buffer -> Effect String
readString = Internal.readString

toString :: Encoding -> Buffer -> Effect String
toString = Internal.toString

write :: BufferValueType -> Number -> Int -> Buffer -> Effect Unit
write = Internal.write

writeString :: Encoding -> Int -> Int -> String -> Buffer -> Effect Int
writeString = Internal.writeString

toArray :: Buffer -> Effect (Array Int)
toArray = Internal.toArray

getAtOffset :: Int -> Buffer -> Effect (Maybe Int)
getAtOffset = Internal.getAtOffset

setAtOffset :: Int -> Int -> Buffer -> Effect Unit
setAtOffset = Internal.setAtOffset

slice :: Int -> Int -> Buffer -> Buffer
slice = Internal.slice

size :: Buffer -> Effect Int
size = Internal.size

concat :: Array Buffer -> Effect Buffer
concat = Internal.concat

concat' :: Array Buffer -> Int -> Effect Buffer
concat' = Internal.concat'

copy :: Int -> Int -> Buffer -> Int -> Buffer -> Effect Int
copy = Internal.copy

fill :: Int -> Int -> Int -> Buffer -> Effect Unit
fill = Internal.fill
144 changes: 120 additions & 24 deletions src/Node/Buffer/ST.purs
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
module Node.Buffer.ST
( STBuffer
, run
, create
, freeze
, unsafeFreeze
, thaw
, unsafeThaw
, fromArray
, fromString
, fromArrayBuffer
, toArrayBuffer
, read
, readString
, toString
, write
, writeString
, toArray
, getAtOffset
, setAtOffset
, slice
, size
, concat
, concat'
, copy
, fill
) where

import Prelude

import Control.Monad.ST (ST, Region)
import Control.Monad.ST as ST
import Node.Buffer.Class (class MutableBuffer, unsafeFreeze)
import Data.ArrayBuffer.Types (ArrayBuffer)
import Data.Maybe (Maybe)
import Node.Buffer (BufferValueType)
import Node.Buffer.Class (class MutableBuffer)
import Node.Buffer.Immutable (ImmutableBuffer)
import Node.Buffer.Internal as Internal
import Node.Encoding (Encoding)

-- | A reference to a mutable buffer for use with `ST`
-- |
Expand All @@ -22,26 +49,95 @@ run :: (forall h. ST h (STBuffer h)) -> ImmutableBuffer
run st = ST.run (st >>= unsafeFreeze)

instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where
create = Internal.create
freeze = Internal.copyAll
unsafeFreeze = Internal.unsafeFreeze
thaw = Internal.copyAll
unsafeThaw = Internal.unsafeThaw
fromArray = Internal.fromArray
fromString = Internal.fromString
fromArrayBuffer = Internal.fromArrayBuffer
toArrayBuffer = Internal.toArrayBuffer
read = Internal.read
readString = Internal.readString
toString = Internal.toString
write = Internal.write
writeString = Internal.writeString
toArray = Internal.toArray
getAtOffset = Internal.getAtOffset
setAtOffset = Internal.setAtOffset
slice = Internal.slice
size = Internal.size
concat = Internal.concat
concat' = Internal.concat'
copy = Internal.copy
fill = Internal.fill
create = create
freeze = freeze
unsafeFreeze = unsafeFreeze
thaw = thaw
unsafeThaw = unsafeThaw
fromArray = fromArray
fromString = fromString
fromArrayBuffer = fromArrayBuffer
toArrayBuffer = toArrayBuffer
read = read
readString = readString
toString = toString
write = write
writeString = writeString
toArray = toArray
getAtOffset = getAtOffset
setAtOffset = setAtOffset
slice = slice
size = size
concat = concat
concat' = concat'
copy = copy
fill = fill

create :: forall h. Int -> ST h (STBuffer h)
create = Internal.create

freeze :: forall h. STBuffer h -> ST h ImmutableBuffer
freeze = Internal.copyAll

unsafeFreeze :: forall h. STBuffer h -> ST h ImmutableBuffer
unsafeFreeze = Internal.unsafeFreeze

thaw :: forall h. ImmutableBuffer -> ST h (STBuffer h)
thaw = Internal.copyAll

unsafeThaw :: forall h. ImmutableBuffer -> ST h (STBuffer h)
unsafeThaw = Internal.unsafeThaw

fromArray :: forall h. Array Int -> ST h (STBuffer h)
fromArray = Internal.fromArray

fromString :: forall h. String -> Encoding -> ST h (STBuffer h)
fromString = Internal.fromString

fromArrayBuffer :: forall h. ArrayBuffer -> ST h (STBuffer h)
fromArrayBuffer = Internal.fromArrayBuffer

toArrayBuffer :: forall h. STBuffer h -> ST h ArrayBuffer
toArrayBuffer = Internal.toArrayBuffer

read :: forall h. BufferValueType -> Int -> STBuffer h -> ST h Number
read = Internal.read

readString :: forall h. Encoding -> Int -> Int -> STBuffer h -> ST h String
readString = Internal.readString

toString :: forall h. Encoding -> STBuffer h -> ST h String
toString = Internal.toString

write :: forall h. BufferValueType -> Number -> Int -> STBuffer h -> ST h Unit
write = Internal.write

writeString :: forall h. Encoding -> Int -> Int -> String -> STBuffer h -> ST h Int
writeString = Internal.writeString

toArray :: forall h. STBuffer h -> ST h (Array Int)
toArray = Internal.toArray

getAtOffset :: forall h. Int -> STBuffer h -> ST h (Maybe Int)
getAtOffset = Internal.getAtOffset

setAtOffset :: forall h. Int -> Int -> STBuffer h -> ST h Unit
setAtOffset = Internal.setAtOffset

slice :: forall h. Int -> Int -> STBuffer h -> STBuffer h
slice = Internal.slice

size :: forall h. STBuffer h -> ST h Int
size = Internal.size

concat :: forall h. Array (STBuffer h) -> ST h (STBuffer h)
concat = Internal.concat

concat' :: forall h. Array (STBuffer h) -> Int -> ST h (STBuffer h)
concat' = Internal.concat'

copy :: forall h. Int -> Int -> STBuffer h -> Int -> STBuffer h -> ST h Int
copy = Internal.copy

fill :: forall h. Int -> Int -> Int -> STBuffer h -> ST h Unit
fill = Internal.fill
3 changes: 2 additions & 1 deletion test/Test/Node/Buffer/Class.purs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import Data.Maybe (Maybe(..))
import Data.Traversable (traverse)
import Effect (Effect)
import Effect.Console (log)
import Node.Buffer (class MutableBuffer, BufferValueType(..), concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, fromString, getAtOffset, read, readString, setAtOffset, slice, thaw, toArray, toArrayBuffer, toString, write)
import Node.Buffer.Class (class MutableBuffer, concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, fromString, getAtOffset, read, readString, setAtOffset, slice, thaw, toArray, toArrayBuffer, toString, write)
import Node.Buffer (BufferValueType(..))
import Node.Buffer.Immutable as Immutable
import Node.Encoding (Encoding(..))
import Test.Assert (assertEqual)
Expand Down
1 change: 0 additions & 1 deletion test/Test/Node/Buffer/ST.purs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module Test.Node.Buffer.ST (test) where

import Prelude

import Control.Monad.ST (run) as ST
import Effect (Effect)
import Effect.Console (log)
import Node.Buffer.Class (create)
Expand Down

0 comments on commit f481921

Please sign in to comment.