TLDR: I am trying to implement the equivalent of this handler struct in Haskell, which involves mutable variables and concurrency and I'm not sure how to go about implementing it
I need to implement the equivalent of this Golang handler in Haskell:
// Handler handler implementation for shim side of chaincode.
type Handler struct {
// serialLock is used to prevent concurrent calls to Send on the
// PeerChaincodeStream. This is required by gRPC.
serialLock sync.Mutex
// chatStream is the client used to access the chaincode support server on
// the peer.
chatStream PeerChaincodeStream
// cc is the chaincode associated with this handler.
cc Chaincode
// state holds the current state of this handler.
state state
// Multiple queries (and one transaction) with different txids can be executing in parallel for this chaincode
// responseChannels is the channel on which responses are communicated by the shim to the chaincodeStub.
// need lock to protect chaincode from attempting
// concurrent requests to the peer
responseChannelsMutex sync.Mutex
responseChannels map[string]chan pb.ChaincodeMessage
}
The snippet above has two things of note:
- It contains mutable state,
state
, which is essentially a FSM to keep track of the current state of the handler - A mutex lock,
responseChannelsMutex
, which is used to lock/unlock theresponseChannels
variable which is accessed by multiple threads.
The handleResponse
function is a typical example of how the responseChannelsMutex
lock is used:
func (h *Handler) handleResponse(msg *pb.ChaincodeMessage) error {
h.responseChannelsMutex.Lock()
defer h.responseChannelsMutex.Unlock()
if h.responseChannels == nil {
return fmt.Errorf("[%s] Cannot send message response channel", shorttxid(msg.Txid))
}
txCtxID := transactionContextID(msg.ChannelId, msg.Txid)
responseCh := h.responseChannels[txCtxID]
if responseCh == nil {
return fmt.Errorf("[%s] responseChannel does not exist", shorttxid(msg.Txid))
}
responseCh <- *msg
return nil
}
There are two parts of porting this handler to Haskell that I am not sure how to do, the state management and the concurrency.
Regarding how to keep mutable state, my first instinct was to reach for the State
/StateT
monad which was covered in the data61/fp-course
.
But after some investigation discovered that the State monad is not thread safe and therefore is not suitable for this use case (I think).
Which lead me looking into ways to handle state in a multi-threaded context...
I have never done any concurrency in Haskell before and am finding all the different options quite overwhelming.
For example:
- MVar vs TVar vs IORef
- forkIO vs async
- CSP vs STM
- The StateM monad as an alternative?
To try and make it less confusing I decided to try and emulate Golang's CSP channel/goroutine approach to the concurrency so I have code to model the architecture off.
But that lead me to finding out that the Control.Concurrent.Chan
, Control.Concurrent.STM
and Control.Concurrent.chp
packages are have different types of channels.
Long story short is that I am overwhelmed trying to figure out how to implement a "handler" pattern in Haskell which uses state and concurrency.
Any guidance/help/recommendations would be greatly appreciated!
For reference, I consider myself a beginner-intermediate Haskeller (I just finished the qfpl/applied-fp-course)