specialization in type classes using ghc
-
11-09-2019 - |
Question
How can I make the genOut/String fire?
module IOStream where
import System.IO
import System.IO.Unsafe
class Out a where
out :: a → String
instance Show a ⇒ Out a where
out = show
outString :: String → String
outString = id
{-# RULES "genOut/String" out = outString #-}
infixl 9 <<, ≪
(≪), (<<) :: Out a ⇒ IO Handle → a → IO Handle
(<<)= (≪)
h ≪ a = do
s ← h
hPutStr s $ out a
return s
cout, cin, cerr :: IO Handle
cout = return stdout
cin = return stdin
cerr = return stderr
endl :: String
endl = "\n"
--infixr 9 ∘ °
(∘) = (.)
(°) = flip (∘)
module Main where
import System.IO
import IOStream
foreign import ccall "pi.h f_" f_ :: IO Double
main :: IO Int
main = do
--putStrLn . show =<< f_
-- ((≪ endl) . (cout ≪)) =<< f_
(cout ≪) ° (≪ endl) =<< f_
return 0
Compile and link:
cetin@unique:~/lab/c/linking/demo$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 6.10.2
cetin@unique:~/lab/c/linking/demo$ ghc -fglasgow-exts -O2 -ddump-simpl-stats -XUndecidableInstances -O2 iostream.hs main.hs pi_v2.o -o hsex2
==================== FloatOut stats: ====================
0 Lets floated to top level; 1 Lets floated elsewhere; from 3 Lambda groups
==================== FloatOut stats: ====================
0 Lets floated to top level; 0 Lets floated elsewhere; from 5 Lambda groups
==================== Grand total simplifier statistics ====================
Total ticks: 184
40 PreInlineUnconditionally
45 PostInlineUnconditionally
24 UnfoldingDone
8 LetFloatFromLet
4 EtaReduction
57 BetaReduction
6 KnownBranch
11 SimplifierDone
==================== FloatOut stats: ====================
0 Lets floated to top level; 0 Lets floated elsewhere; from 1 Lambda groups
==================== FloatOut stats: ====================
3 Lets floated to top level; 0 Lets floated elsewhere; from 1 Lambda groups
==================== Grand total simplifier statistics ====================
Total ticks: 218
42 PreInlineUnconditionally
57 PostInlineUnconditionally
33 UnfoldingDone
9 LetFloatFromLet
1 EtaReduction
66 BetaReduction
10 KnownBranch
12 SimplifierDone
Result:
cetin@unique:~/lab/c/linking/demo$ ./hsex2
3.141592653589793"\n"cetin@unique:~/lab/c/linking/demo$
Expected:
cetin@unique:~/lab/c/linking/demo$ ./hsex2
3.141592653589793
cetin@unique:~/lab/c/linking/demo$
How can I make that rule fire?
Solution
It looks like you want overlapping instances, as you want to have an instance Out String
which is different than instance Show a => Out a
which overlaps it.
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
{-# LANGUAGE OverlappingInstances, UndecidableInstances #-}
class Out a where out :: a -> String
instance Out String where out = id
instance (Show a) => Out a where out = show
The general recommendation is to avoid use of overlapping instances and undecidable instances unless truly necessary, as the changes to typechecking that they make are not portable and can cause other issues.
Edit
Implementation-wise, think of dictionary for each instance of a class. <<
will be given the dictionary for the instance of Out
that it is expected to use as a hidden parameter. Since out
is being looked up from there, there's no room for your RULE
to fire. If out
weren't in a typeclass, and was being called from a non-polymorphic function, then I would expect the RULE
to match, but as is, it's not surprising that it doesn't work.
RULE
/SPECIALIZE
are meant to be optimizations only, and your code should not change behavior if they do or do not fire.
It took me a while to realize what you were doing… you do realize that Haskell is not C++, right? The ways in which polymorphism is utilized is quite different.