3 ms·
Thanks for the recommendation! I've run across it in the past, but I still find Haskell syntax to be absolutely baffling so I was put off by the learning curve.
by thrtythreeforty 5y ago
Thanks for the recommendation! I've run across it in the past, but I still find Haskell syntax to be absolutely baffling so I was put off by the learning curve. (There's something to be said for sticking to syntax familiar to users of the incumbent - Rust vs C++ springs to mind.)
Perhaps I'll give Clash a shot - the homepage describes basically all the features I'm looking for. The documentation appears to be excellent.
- wyager 5y agoThere’s definitely a learning curve, but since you’re using a relatively restricted subset of Haskell, you can basically skip learning about things like IO and also most of the Haskell ecosystem, so it’s very manageable. The learning curve is sort of inherent to the fact that the reason Clash works so well is that semantics of a pure lazy functional language with algebraic data types (very different from what most people learn) almost exactly matches the standard 01XZ semantics of digital circuitry. There’s one very small difference that I’m aware of. The nearly-ideal correspondence is honestly kind of surprising.
- melony 5y agoWhat is the small difference?
- wyager 5y agoZero-bit busses cannot have an indeterminate value in hardware. However, zero-bit values (Unit) can have indeterminate value in software. If you have the circuit f :: () -> Int f () = 3 Vs g :: () -> Int g _ = 3 Are the exact same thing in hardware but different in software. “f undefined” is undefined in software but “g undefined” is not. Neither is undefined in hardware. This distinction comes up very rarely. Only if you have a parametric circuit that sometimes takes zero-bit inputs (I.e. if the input is unused in some instantiations).
- a1369209993 5y agoIIUC, this is actually a general deficiency in the type system for variable-width tuples. Consider: f1 :: (Bit,()) -> Int f1 (0,()) = 3 f1 (1,()) = 3 -- f1 (0,undefined) is undefined f2 :: (Bit,(Bit,())) -> Int -- and so on. Conversely g2 :: Bit -> Bit -> Int g1 :: Bit -> Int g0 :: Int -- doesn't have this problem but also doesn't work well for busses. -- The 'obvious' approach h3 :: (Bit,Bit,Bit) -> Int h2 :: (Bit,Bit) -> Int h1 :: (Bit) -> Int h0 :: () -> Int -- doesn't work generically; (Bit) isn't a tuple at all, -- and n-element tuples are totally unrelated to m(!=n)-element tuples -- (they're literally declared as: data (a,b) = (a,b) data (a,b,c) = (a,b,c) -- in module GHC.Tuple) You can actually fix this by (ab)using types of kind `TYPE 'UnliftedRep` and unboxed tuples (type `(# Car, Cdr #)`, value `(# b7, b6543210 #)`), it's just generally not worth the effort.
- JoelMcCracken 5y agoInteresting. How does non-strict evaluation match?
- wyager 5y agoBecause in hardware you can have undefined states (bottom) and it’s fine as long as you don’t observe the undefined state (pattern match on bottom). The only difference is pattern matching on Unit, which is represented by a zero-width bus in hardware. Beyond that, non-termination “contaminates” a value just like undefinedness “contaminates” a circuit.