How to use the gi-secret package?

I’d like to use the gi-secret package to manage secrets in the gnome keyring running on my linux machine. Here is an example invocation of passwordLookupSync in ghci which fails.

...
λ> import GI.Secret.Functions

λ> passwordLookupSync Nothing (Map.fromList [("key", "value")]) Nothing

<interactive>:2:1: error:
    • Couldn't match representation of type ‘a0’
                               with that of ‘haskell-gi-base-0.26.8:Data.GI.Base.BasicTypes.ManagedPtr
                                               ()’
        arising from a superclass required to satisfy ‘Coercible
                                                         a0
                                                         (haskell-gi-base-0.26.8:Data.GI.Base.BasicTypes.ManagedPtr
                                                            ())’,
        arising from a superclass required to satisfy ‘haskell-gi-base-0.26.8:Data.GI.Base.BasicTypes.ManagedPtrNewtype
                                                         a0’,
        arising from a superclass required to satisfy ‘haskell-gi-base-0.26.8:Data.GI.Base.BasicTypes.GObject
                                                         a0’,
        arising from a use of ‘passwordLookupSync’
    • In the expression:
        passwordLookupSync
          Nothing (Map.fromList [("key", "value")]) Nothing
      In an equation for ‘it’:
          it
            = passwordLookupSync
                Nothing (Map.fromList [("key", "value")]) Nothing

I am getting the same error during compilation of my program.
I am using ghc 9.4.8, the highest version which I managed to compile haskell-gi packages. From cabal freeze output here is the list of gi packages used:

any.gi-gio ==2.0.35,
any.gi-glib ==2.0.30,
any.gi-gobject ==2.0.31,
any.gi-secret ==0.0.18,
any.haskell-gi ==0.26.12,
any.haskell-gi-base ==0.26.8,
any.haskell-gi-overloading ==1.0,

I am not too familiar with the gnome/gobject/glib/... ecosystem. Could you, please, give me some pointers how to properly use the functions from GI.Secret.Functions module?

Wow, that’s an incomprehensible error message. This is what you need to do:

> import GI.Gio.Objects.Cancellable
> passwordLookupSync Nothing (Map.fromList [("key", "value")]) (Nothing :: Maybe Cancellable)

(Then I get a null pointer error, but that’s a domain error, not a Haskell problem.)


By the way, I had to install these packages:

sudo apt-get install libsecret-1-dev libgirepository1.0-dev
1 Like

Thank you, that works!

Would you mind to shed some light on your deciphering the error message?

One thing that I noticed was the occurrence of () in the error message. I think that is due to the extended default rules present in GHCi. Otherwise GHC would have warned you about ambiguity of your use of Nothing.

Edit: but that is not actually true. The () only occurs in the combination ManagedPtr (), which originates from here: GI.Gio.ManagedPtrNewtype

Anyway, I guess you should be careful with constructors for polymorphic data types like [] and Nothing which can easily cause ambiguity.

Edit2: My diagnosis is this:

The passwordLookupSync function has the type:

:: (HasCallStack, MonadIO m, IsCancellable a)	 
=> Maybe Schema
-> Map Text Text
-> Maybe a
-> m Text

For the type variable a to be IsCancellable, it must be a GObject which in turn means it must be ManagedPtrNewtype which finally requires that the type is coercible to ManagedPtr ().

You haven’t given it a concrete type annotation and just used Nothing which is polymorphic in a, so GHC doesn’t know that the coercion is valid.

Here’s a simpler example of the same error:

import Data.Coerce

foo :: Coercible a Bool => Maybe a -> Bool
foo Nothing = False
foo (Just x) = coerce x

bar :: Bool
bar = foo Nothing
Err.hs:8:7: error:
    • Couldn't match representation of type ‘a0’ with that of ‘Bool’
        arising from a use of ‘foo’
    • In the expression: foo Nothing
      In an equation for ‘bar’: bar = foo Nothing
  |
8 | bar = foo Nothing
  |       ^^^

Interestingly, this error is specific to the Coercible constraint. If you use another constraint you get a slightly more useful error:

import Data.Coerce

foo :: Show a => Maybe a -> String
foo Nothing = ""
foo (Just x) = show x

bar :: String
bar = foo Nothing
Err.hs:8:7: error:
    • Ambiguous type variable ‘a0’ arising from a use of ‘foo’
      prevents the constraint ‘(Show a0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘a0’ should be.
      Potentially matching instances:
        instance Show Ordering -- Defined in ‘GHC.Show’
        instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
        ...plus 25 others
        ...plus 12 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the expression: foo Nothing
      In an equation for ‘bar’: bar = foo Nothing
  |
8 | bar = foo Nothing
  |       ^^^

Edit: I’ve created a GHC issue to make this error message clearer: #25416.

2 Likes

Thank you for the detailed explanation.

Actually, there is another ambiguity due to MonadIO which is hidden in GHCi but comes out when using the call in a program. That is easy to deal with.

(Then I get a null pointer error, but that’s a domain error, not a Haskell problem.)

I think it is a bit more complicated and due to some discrepancy between GI stuff and the haskell bindings. There are at least two open issues related to this #249 and #250. It definitely is not nice to report a missing key by a nullPtr exception.

Now I am going to implement callings the gi-secret API in my program to see how it goes. I’ll come back to report, maybe there are some (design) lessons to learn here. My hunch is that eventually I would stick to my old more robust API to the keyring service.

1 Like

Meanwhile, Iñaki, one of the maintainers of haskell-gi, told me that while investigating my problem he also fixed this and released a new
version: gi-secret-0.0.19. The new version works fine returning Nothing in the case of missing key.

1 Like

Since the error message was about a type variable a0 I guessed the problem was that it was ambiguous. I looked up the definition of passwordLookupSync and indeed, if you supply Nothing for the Maybe a then a will be ambiguous.

That means we need to specify a type for a. Which one? Well, it has to be IsCancellable. Weirdly IsCancellable is not linked to any Haddock, but Hoogle points us to the right place. The “Instances details” don’t give a direct answer, but since it’s for things that can be cast to Cancellable I just guessed that Cancellable itself would work, which it did.

2 Likes

To anyone who is interested in helping improve the error messages, I’ve proposed a new error message and there are some hints on which part of GHC will need to be changed in this GitLab issue:

It has been labeled “newcomer” so please try your hand at it and feel free to ask questions if you get stuck.

4 Likes