Specialize pragmas - Haskell

Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.

Bolt

Hi guys! I want to write an Enum instance for tuples and I want to have a faster version if the second component of the tuple is an Int how do I do that? I searched the specialize pragma, but didn't understand how to use it

Bolt

and how does it work

Bolt
instance
  ( Enum a,
    Enum b,
    Bounded b
  ) =>
  Enum (a, b)
  where
  toEnum i = let (listB :: [b]) = [minBound..maxBound]
                 lengthB = length listB
                 fstI = div i lengthB
                 sndI = mod i lengthB
              in (toEnum fstI, toEnum sndI)

toEnumInt :: (Enum a) => Int -> (a, Int)
toEnumInt i = let lengthInt = maxBound - minBound
                  fstI = div i lengthInt
                  sndI = mod i lengthInt
               in (toEnum fstI, toEnum sndI)

I have this and I want to use toEnumInt in the specialized version

Bolt

Is it specialized or rewrite rule pragma that I want?

Bolt

Is it specialized or rewrite rule pragma that I want?

Jack Henahan

I think if you want to write the function yourself, you want a rewrite rule

Jack Henahan

The specialize pragma just generates a specialized version of the function along with a rewrite rule to use the generated specialized version

Bolt

I am trying with rewrite rule but I get this error

[ghcmod] [E]  Could not deduce (Enum a) arising from a use of toEnumInt
  from the context: Enum (a, Int)
    bound by the RULE "toEnum/Int"
    at /tmp/ghc-mod21790/Internal21789-1.hs:26:11-41
  Possible fix: add (Enum a) to the context of the RULE "toEnum/Int"
 In the expression: toEnumInt
  When checking the transformation rule "toEnum/Int"
Jack Henahan

What's your rule look like?

Jack Henahan

Oh, actually, you probably just have to stick an Enum a on it

Jack Henahan

@Bolt Example: https://stackoverflow.com/a/25326480

I've added the following rewrite rule to conduit without issue: {-# RULES "ConduitM: lift x >>= f" forall m f. lift m >>= f = ConduitM (PipeM (liftM (unConduitM . f) m)) #-} I'm
Jack Henahan

Oh, although it may not actually fire because Core is silly. Maybe you need ifcxt like in https://stackoverflow.com/a/25326480

I've added the following rewrite rule to conduit without issue: {-# RULES "ConduitM: lift x >>= f" forall m f. lift m >>= f = ConduitM (PipeM (liftM (unConduitM . f) m)) #-} I'm
Jack Henahan

I guess try the first way and use -ddump-rule-firings to see if it actually works, then go down the other route if not

Sandy Maguire

@Bolt giving errors is not very helpful without the code that caused the error in the first place. food for thought in the future when asking questions

Sandy Maguire

this thing is not a SPECIALIZE --- that just tells GHC to optimize a definition with a specific type signature

Sandy Maguire

i would further describe this as "don't put in the effort"

Sandy Maguire

rewrite rules are tricky to get right and often lead to pain

Sandy Maguire

instead what you really want to do is give an OVERLAPPING instance of Enum a => Enum (a, Int)

Sandy Maguire

GHC can definitely internally create rules of the form you're asking about, but i'm not sure the language exposed to source haskell is powerful enough to bind dictionaries

Bolt

I will keep that in mind!

Nice @Sandy Maguire ! Your suggestion worked! Haskell can have weird things, thanks for the help!