DEV Community

Simon Shine
Simon Shine

Posted on

1 1

Aggressive refactoring

Here is a small example of aggressive refactoring in Haskell. I am in the codebase of a compiler for a small smart contract language, and I'm replacing occurrences of Integer with occurrences of Word256, since the code generator in question targets Ethereum which has 256-bit word sizes.

The compiler was originally made with Integer, and in some cases its very own Word256 which was made in a crude way. Integer because this is a very convenient data type in Haskell, even though arbitrary precision in this case is actually wrong, and the crude Word256 because it seemed simpler to make this type rather than import a library.

But now this is no longer convenient, so I've decided to use Word256 of the data-dword library since that's what Hevm uses. There are alternatives: largeword and wide-word, but the important part for now is interoperability.

And here is the type error I'd like to show:

src/Lira/Backends/Evm/EvmCompiler.hs:695:22: error:
    • Couldn't match expected type ‘Integer’
                  with actual type ‘Data.DoubleWord.Word256’
    • In the first argument of ‘push’, namely ‘i’
      In the expression: push i
      In the expression: [push i]
    |
695 |   IntVal  i -> [push i]
    |                      ^
Enter fullscreen mode Exit fullscreen mode

Because IntVal i now contains a Word256 and push was designed for Integer, the types no longer align. Looking at push:

push :: Integer -> EvmOpcode
push = PUSHN . words'
  where
    words' :: Integer -> [Word8]
    words' i | i < 256 = [fromIntegral i]
    words' i           = words' (i `div` 256) ++ [fromIntegral $ i `mod` 256]
Enter fullscreen mode Exit fullscreen mode

There is no logic in this function that prevents it from operating on Word256 rather than Integer. In fact, rather than change the function, just loosen the type signature:

-push :: Integer -> EvmOpcode
+push :: Integral i => i -> EvmOpcode
 push = PUSHN . words'
   where
-    words' :: Integer -> [Word8]
+    words' :: Integral i => i -> [Word8]
     words' i | i < 256 = [fromIntegral i]
     words' i           = words' (i `div` 256) ++ [fromIntegral $ i `mod` 256]
Enter fullscreen mode Exit fullscreen mode

Doing an aggressive refactor like changing a base type in the codebase requires hundreds of changes. But when their nature is like this, I can safely leave this change be and proceed to the next type error.

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay