1

I read this blog post on how to create a custom Prelude library. The library can be found here. One of the things it does is prohibit String. It also defines a function for automatic string conversions (here). I have enabled OverloadedStrings in the cabal file.

Before using this library I had:

data Point = Point Int Int
instance Show Point where
  show (Point x y) = "(" ++ show x ++ ", " ++ show y ++ ")"

After using the library it says: "show' is not a (visible) method of classShow'"

So I resorted to create a custom function to show the data type:

showPoint :: Point -> LText
showPoint (Point x y) = toS ("(" ++ show x ++ ", " ++ show y ++ ")")

The compiler is saying that the use of toS, "(", show is ambiguous, but I don't understand why. Do I have to do something like what is proposed here?

Edit:

Had to disable OverloadedStrings and change the code to the following:

showPoint :: Point -> LText
showPoint (Point x y) = toS "(" <> show x <> toS ", " <> show y <> toS ")"

Wondering if it is possible to do the same without disabling OverloadedStrings so I don't have to use toS for every String.

OneEyeQuestion
  • 742
  • 7
  • 22

2 Answers2

2

This is what worked for me:

{-# LANGUAGE OverloadedStrings #-}

module Test where

import Protolude
import qualified Base as PBase

data P = P Int

instance PBase.Show P where
  show (P x) = "a P " ++ show x

Update

The protolude implemention of show is as a normal function (see the end of Protolude.hs):

show :: (Show a, StringConv String b) => a -> b
show x = toS (PBase.show x)

So you need a PBase.Show instance in order use protolude's show function.

Also protolude's show can return any of the string types, so you're not forcing others to use of String by defining a PBase.show instance.

Update #2

You can import the typeclass show function from GHC.Show:

{-# LANGUAGE NoImplicitPrelude #-}

import Protolude
import GHC.Show

data P  = P Int

instance Show P where
  show (P x) = "<-- P " ++ GHC.Show.show x ++ " -->"

main = print (P 123)
ErikR
  • 51,541
  • 9
  • 73
  • 124
  • But wouldn't that defeat the purpose of hiding Show? Or I am just misinterpreting the use of Protolude library? That would be returning a `String`, if I am not mistaken, so it would reintroduce the hidden type that the library wanted to avoid. – OneEyeQuestion Jun 05 '16 at 17:24
  • So the return type of `show` is inferred to `LText`? How does `show` interact with the SPECIALIZE pragmas? – OneEyeQuestion Jun 05 '16 at 17:53
  • 1
    The return type of `PBase.show` is always `String`. The return type of protolude's show can be Text or LText or String or any other type `b` for which `StringConv String b` holds. SPECIALIZE just tells GHC to create monomorphic implementations of the function for certain types. This can make those routines run faster - otherwise the polymorphic code is executed. – ErikR Jun 05 '16 at 19:38
  • Do you have to do anything special to import `import qualified Base as PBase` ? It says it is a hidden module in the package protolude. – OneEyeQuestion Jun 05 '16 at 20:50
  • Your solution would resolve to `String`, right? Is it possible to force `show` function from `Show Instance` to always resolve to LText, or Bytestring without passing through `String` ? – OneEyeQuestion Jun 05 '16 at 22:17
  • 1
    The PBase.show typeclass function _always_ returns a String. But when you call Protolude's `show` you can specify what the return type should be. The definition of Protolude's `show` is fixed - you cannot change the way it works. It always will call the value's Pbase `show` function and convert the String to whatever result type is requested. – ErikR Jun 05 '16 at 22:28
  • Just curious, do you think that is a performance efficient implementation? that is one of the expectations I had behind using Protolude and its removal of String. – OneEyeQuestion Jun 05 '16 at 22:44
1

It could be caused by the following:

  • show can produce any string-like type
  • string literals are overloaded, so they are of any string-like type
  • toS can accept many different types

If so, the compiler does not know which intermediate string-like type to pick, and an ambiguity error is thrown.

chi
  • 111,837
  • 3
  • 133
  • 218
  • Is it possible to make the strings literals infer the type `LText`? Do I have to remove the Overloaded strings extension? – OneEyeQuestion Jun 05 '16 at 17:16
  • If you mean `toS (("(" :: LText) ++ show x ++ (", " :: LText) ++ show y ++ (")" :: LText))`, then the compiler is still thinking that "..." has type [a]. – OneEyeQuestion Jun 05 '16 at 17:40
  • 1
    @OneEyeQuestion Uhm.. please post the full error. Only one annotation should be enough (the others are then inferred). Which is the type of the `show` you are using, by the way? – chi Jun 05 '16 at 17:59
  • 1
    I solved it. Was my fault. Thought (++) was also available with `Text`. – OneEyeQuestion Jun 05 '16 at 18:03
  • Tried this: `toS ("My Sample Text" :: LText)` but I get this error: No instance for (StringConv a0 LText) arising from a use of `toS' The type variable `a0' is ambiguous Note: there are several potential instances: ... In the expression: toS "My Sample Text" – OneEyeQuestion Jun 05 '16 at 21:01