Dear Sumit,
You are right that there's something's fishy about the free monadic modeling of accept.
The following parallel construction will prove instructive:
The native effect:
send :: chan -> ByteString -> IO Bool
is modeled in the free monad by the constructor for the base functor
Send :: chan -> ByteString -> (Bool -> next) -> NetworkActivity chan next
which is the data wrapping used in the value level
sendit :: chan -> ByteString -> Free (NetworkActivity chan) Bool
sendit chan buf = liftF (Send chan buf identity)
Analogously, the native
accept :: chan -> IO chan
is modeled by
Accept :: chan -> (chan -> next) -> NetworkActivity chan next
used in
acc :: chan -> Free (NetworkActivity chan) chan
acc chan = liftF (Accept chan identity)
Except that you used a different constructor for the base functor. Not
Accept :: chan -> (chan -> next) -> NetworkActivity chan next
but
Accept :: chan -> next -> (chan -> next) -> NetworkActivity chan next
which is equivalent to
Accept :: chan -> (Maybe chan -> next) -> NetworkActivity chan next
The new free monadic term that substitutes for the native accept is the same like before
acc chan = liftF (Accept chan identity)
only with a different type
acc :: chan -> Free (NetworkActivity chan) (Maybe chan)
modeling a native
accept :: chan -> IO (Maybe chan)
Given a native API, its free monad encoding is entirely boilerplate. I wrote about the boilerplate process here (skip the sections that don't concern you):
Best, Kim-Ee