探究无需为局部绑定board添加类型签名,使Clash玩具模型代码通过GHC类型检查的可行方案
board while using withClock in Clash? Hey there! Let's work through how to make this code pass type checking without adding a type signature to board. The core issue here is that when you bind board as a separate variable, GHC tries to infer its most general type in isolation—without automatically picking up the HiddenClock DomSys constraint that mainBoard and peripherals require. Since withClock expects a value that carries this constraint, GHC gets stuck because it can't tell board is supposed to have it.
The simplest fix: Inline the board definition
Instead of binding board separately, pass its logic directly as the argument to withClock. This lets GHC use withClock's type signature to infer that the expression needs the HiddenClock DomSys constraint, no explicit type signature required. Here's the modified topEntity:
topEntity :: Clock Dom25 -> Clock DomSys -> Signal DomSys -> Signal Dom25 topEntity clkVid clkSys input = vga where (vga, vidRead, line) = video clkVid clkSys vidAddr vidWrite (vidAddr, vidWrite) = withClock clkSys $ mainBoard vidRead line (peripherals input)
This works because when you pass mainBoard vidRead line (peripherals input) directly to withClock, GHC sees that withClock expects a value of type HiddenClock dom => r. It unifies dom with DomSys (from clkSys :: Clock DomSys) and infers that the expression must satisfy HiddenClock DomSys to match withClock's requirements.
Why the original approach failed
When you bind board = mainBoard vidRead line p separately, GHC infers its type without any context linking it to withClock's constraint. It sees mainBoard and peripherals need HiddenClock dom, but without a hint that dom should be DomSys or that this constraint is required for the withClock call, it can't automatically attach the constraint to board's type. The explicit type signature fixes this by telling GHC "this value only exists when HiddenClock DomSys is in scope".
Alternative: Keep structure with a lambda
If you want to retain the p binding for readability, wrap the logic in a lambda inside withClock. This is functionally identical to inlining and still avoids a type signature for the core board logic:
topEntity :: Clock Dom25 -> Clock DomSys -> Signal DomSys -> Signal Dom25 topEntity clkVid clkSys input = vga where (vga, vidRead, line) = video clkVid clkSys vidAddr vidWrite (vidAddr, vidWrite) = withClock clkSys $ let p = peripherals input in mainBoard vidRead line p
Would modifying withClock help?
Your original withClock type is already correct for this pattern. While you could use type applications to explicitly specify DomSys when calling it (withClock @DomSys clkSys board), this still requires a type signature for board, so it doesn't solve the core goal of avoiding the signature. The inline approach is far cleaner here.
内容的提问来源于stack exchange,提问作者Cactus

