Abstract FilePath Proposal

Mario Blažević blamario at ciktel.net
Mon Jun 29 02:24:01 UTC 2015

On 06/27/2015 09:37 AM, Herbert Valerio Riedel wrote:
> On 2015-06-27 at 14:56:33 +0200, David Fox wrote:
> [...]
>> ​I've had success with a slightly different "How":
> What was your concrete use-case btw?
>> Phase 1: Replace FilePath with a type class, with instances for the old
>> FilePath (i.e. String) and the new implementation.
> what would that comprise in the FilePath case?
> I assume adding a transitional class whose methods are not exposed (and
> whose typeclass name is exported from some GHC-specific internal-marked
> module)? i.e.
>    class IsFilePath a where
>      privateToFilePath :: a -> FilePath
>      privateFromFilePath :: FilePath -> a
>    instance IsFilePath FilePath where
>      privateToFilePath   = id
>      privateFromFilePath = id
>    instance IsFilePath [Char] where
>      privateToFilePath   = System.IO.toFilePath
>      privateFromFilePath = System.IO.fromFilePath

It's probably better to not export the class at all, only its two 
instances and its methods. See below.

> as well as changing a lot of type-sigs in base & filepath from
> e.g.
>    writeFile :: FilePath -> String -> IO ()
>    openTempFile :: FilePath -> String -> IO (FilePath, Handle)
> to
>    writeFile    :: IsFilePath a => a -> String -> IO ()
>    openTempFile :: IsFilePath a => a -> String -> IO (a, Handle)
> ?
>> Phase 2:  Wait until a suitable amount of hackage builds without the string
>> instance.
> I can see Stackage helping with that by using a custom GHC which lacks
> the legacy `IsFilePath [Char]`-instance. So I'd be optimistic that Phase2 could be
> accomplished within one year for the Stackage-subset of Hackage.
>> Phase 3: Deprecate the String instance - move it to an old-filepath package.
>> Phase 4: Replace the type class with the new implementation
> I assume this means getting rid again of the typeclass, and changing the
> type-sigs back to i.e.
>    writeFile :: FilePath -> String -> IO ()
>    openTempFile :: FilePath -> String -> IO (FilePath, Handle)
>> (but now with with the new opaque `FilePath`)?
>> This way the new implementation is available immediately, packages can
>> begin converting at once, benefits can be assessed.
> This scheme seems feasible at first glance, as long as the typeclass
> doesn't start spreading across packages and find its way into type-sigs
> (in which case it'd become more disruptive to get rid of it
> again). Otoh, I'm not sure (assuming I understood how your scheme works)
> it can be avoided to have the typeclass spread, since if not every API
> that now has `FilePath` arguments in their type-sigs gets generalised to
> have `IsFilePath a => a` arguments instead, we can't reach the goal of
> "Phase 2".

     As long as the typeclass itself is not exported, this is not a big 
problem. Every *explicit* type signature would have to contain either a 
String or a FilePath. Yes, the transition to the FilePath-only state 
would probably be slower, but that is the intended feature, not a bug.

     GHC could do more to help with the transition if it allowed the 
DEPRECATED pragma on class instances:

instance IsFilePath String {-# DEPRECATED "Use the FilePath type 
instead" #-} where ...

     The warning would be reported wherever the compiler can statically 
determine that the deprecated instance is used.

More information about the Libraries mailing list