Question about "translucent sealing" in the Backpack paper

I am trying to read the Backpack paper.

Here is a paragraph I am trying to understand:

A key reason we can get by with a simpler semantics of linking is that we are deliberately less ambitious than MixML in a certain sense: unlike MixML, we do not aim to completely subsume the
functionality of ML modules. MixML does, and this means that its semantics must deal with nested uses of translucent sealing (i.e., the ability to define types that are “transparent” inside a module but but “opaque” outside), a defining feature of ML modules which compounds the already-tricky double-vision problem. In contrast, does not attempt to support translucent sealing—and does not suffer the attendant complexities—for the simple reason that Haskell, our target of elaboration, cannot support it.

I find this confusing, as my initial interpretation of the phrase “translucent sealing” was that it refers to something equivalent to the concept of “abstract data type” outlined in section 11.2 of the Haskell tutorial here.

However, the paper says that Haskell does not support translucent sealing, so this cannot be right.

My question is: What is translucent sealing in this context and how does it differ from abstract data types?

4 Likes

The system described in the original Backpack '14 paper does not correspond exactly to what was eventually implemented. Edward Z. Yang’s (very readable) thesis is much closer to the actual implementation. It mentions translucency:

IIUC, this could be a valid example. The abstract data type Str from the signature is implemented with a type synonym here and here. It would also work without a synonym if the types had the exact same name.

In the example, the indefinite library only knows about Str what is listed in the signature. But the Main module, which consumes an “instantiated” (in two different ways) version of the library, knows that Str is String and Text, respectively.

2 Likes

In really-small-backpack-example the Understanding and Evolving the ML Module System talks about translucent sealing. My take on it is that you can “pass a type” to a module to “instantiate” the non opaque parts. An abstract data type is opaque and not translucent.

I guess translating to Haskell would be like being able to have “data Foo a = Foo a Secret” and being able to get a Foo by giving the constructor just an Int. But no, you need to either make Foo opaque and export a custom constructor (so the implementation can shove the Secret in), or make Foo transparent and Secret transparent or provide something to build Secrets, when you don’t want to involve the consumer with anything related to Secret, since it’s an implementation detail.

Question. Why not do?

module Interface where

class Str a where
    splitOn :: Char -> a -> [a]
module TextImpl where

import Interface
import qualified Data.Text as T

instance Str T.Text where
    splitOn c = T.splitOn (T.singleton c)
module StringImpl where

import Interface
import qualified Data.List.Split as S

instance Str String where
    splitOn c = S.splitOn [c]

What are the consequences of this?

1 Like

This typeclass-based approach is the usual one. The introductory chapter of Edward Z. Yang’s thesis mentions some of its disadvantages, as way to motivate Backpack:

I would add that with the typeclass-based approach,

  • the library that defines the typeclass must depend on the library that defines the implementation type, or
  • the library that defines the implementation type must depend on the library that defines the typeclass, or
  • we define orphan instances, or
  • we define some kind of newtype wrapper to avoid the orphan instances.

Backpack avoids the coupling inherent in the first two points, the risks of the third, and the inconvenience of the fourth. But of course, it has disadvantages of its own.

1 Like

Thank you.

About point 1: I feel that with careful interface crafting you can can a lot of mileage out of it. You can also try subsuming complicated constraints.
class (Maker a, Regex a, Comp a, Exec a) => RegexSource a where

About point 2: you can say this about interfaces in any language, to be honest. You always pay a price for poor interfaces and premature abstraction.

About point 3: this I agree 100% and believe it a 220% justification for backpack.

I believe peak typeclass performance is having an interface package that implementations import and instance.
So you have package Interface with modules:

module v1.Str where

class Str a where
    f1
    f2
    f3
module v2.Str where

class Str a where
    f1
    f2
    f3 -- DEPRECATION WARNING
    f4
    f5
module v3.Str where

class Str a where
    f1
    f2
    f4
    f5

and you can go removing modules as the package hits a major version change, too. Then the user imports the relevant interface and the relevant implementation. Of course, this is bogus if you have hyperchanging typeclasses all over the place.

It’s clear that the solution to typeclass problems is more typeclasses and that’s a problem (as per point 3).