GHC does not select the only available instance

I am trying to write CSS DSL in Haskell and keep the syntax as close to CSS as possible. One of the difficulties is that certain terms can be displayed as a property and value. For example flex: you can have "display: flex" and "flex: 1" in CSS.

I allowed myself to inspire the Lucid API, which redefines functions based on function arguments, to generate any attributes or DOM nodes (which sometimes also use names, for example <style>and <div style="...">).

Anyway, I ran into a problem that the GHC cannot check the type of code (a variable of type Ambiguous) in the place where it is supposed to select one of the two available instances of typeclass. There is only one instance that fits (and indeed, in a GHC type error it prints “This potential instance exists” and then it lists only one). I am confused by the fact that, given the choice of one instance, the GHC refuses to use it. Of course, if I add explicit type annotations, then code compilation. Full example below (dependency only - mtl, for Writer).

{-# LANGUAGE FlexibleInstances #-}
module Style where

import Control.Monad.Writer.Lazy


type StyleM = Writer [(String, String)]
newtype Style = Style { runStyle :: StyleM () }


class Term a where
    term :: String -> a

instance Term String where
    term = id

instance Term (String -> StyleM ()) where
    term property value = tell [(property, value)]


display :: String -> StyleM ()
display = term "display"

flex :: Term a => a
flex = term "flex"

someStyle :: Style
someStyle = Style $ do
    flex "1"     -- [1] :: StyleM ()
    display flex -- [2]

And the error:

Style.hs:29:5: error:
     Ambiguous type variable ‘a0’ arising from a use of ‘flex’
      prevents the constraint ‘(Term
                                  ([Char]
                                   -> WriterT
                                        [(String, String)]
                                        Data.Functor.Identity.Identity
                                        a0))’ from being solved.
        (maybe you haven't applied a function to enough arguments?)
      Probable fix: use a type annotation to specify what ‘a0’ should be.
      These potential instance exist:
        one instance involving out-of-scope types
          instance Term (String -> StyleM ()) -- Defined at Style.hs:17:10
     In a stmt of a 'do' block: flex "1"
      In the second argument of ‘($)’, namely
        ‘do { flex "1";
              display flex }’
      In the expression:
        Style
        $ do { flex "1";
               display flex }
Failed, modules loaded: none.

I found two ways to make this code compiled, none of which I like.

  • Add an explicit annotation that uses the flex function ([1]).
  • Move the line where flex is used at the end of the do block (for example, comment [2]).

API Lucid , Lucid , Lucid fundeps, , -, GHC typechecker ( typeclass). ( ).

+4
2

, Term String -> StyleM () , StyleM (). do-block,

someStyle :: Style
someStyle = Style $ do
    flex "1"
    return ()

, , flex "1", .

" " . , {-# LANGUAGE TypeFamilies #-}   {-# LANGUAGE GADTs #-} :

{-# LANGUAGE TypeFamilies #-}

instance (a ~ ()) => Term (String -> StyleM a) where
    term property value = tell [(property, value)]

: " a, , ! , , , () !"

- . " , , ". , , .

, Haskell , , "", , , .

+13

, ( , GHC " ", ). , , , GHC .

; . GHC , . (, , OverlappingInstances.) , " C T" - "" " ". "" , C T.

, , , -, " "? : , , . , , , , , ; .

, C , , ,

Left True == Left False

Eq (Either Bool t),

instance Eq (Either Bool t)
instance Eq (Either a t)    -- *
instance Eq (f Bool t)
instance Eq (f a t)
instance Eq (g t)
instance Eq b

( * , , Haskell ( FlexibleInstances) , ; C (T var1 ... varN) , .)

-, , Eq (, , ).

( =>). "" , ( =>). . ( , , instance Foo a => Bar a .)

, do, Term (String -> StyleM a). Term (String -> StyleM ()) , .

do
  () <- flex "1"
  ...

, .

+6

Source: https://habr.com/ru/post/1663444/


All Articles