Content-Length: 813723 | pFad | http://github.com/purescript/purescript/pull/3824/files

C1 Support deriving via by kl0tl · Pull Request #3824 · purescript/purescript · GitHub
Skip to content

Support deriving via #3824

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions lib/purescript-ast/src/Language/PureScript/AST/Declarations.hs
Original file line number Diff line number Diff line change
Expand Up @@ -440,19 +440,34 @@ pattern ValueFixityDeclaration sa fixity name op = FixityDeclaration sa (Left (V
pattern TypeFixityDeclaration :: SourceAnn -> Fixity -> Qualified (ProperName 'TypeName) -> OpName 'TypeOpName -> Declaration
pattern TypeFixityDeclaration sa fixity name op = FixityDeclaration sa (Right (TypeFixity fixity name op))

data DerivingStrategy
= DeriveNewtype
| DeriveVia SourceType
deriving (Show)

-- | The members of a type class instance declaration
data TypeInstanceBody
= DerivedInstance
= DerivedInstance (Maybe (DerivingStrategy))
-- ^ This is a derived instance
| NewtypeInstance
-- ^ This is an instance derived from a newtype
| NewtypeInstanceWithDictionary Expr
-- ^ This is an instance derived from a newtype, desugared to include a
-- dictionary for the type under the newtype.
| ViaInstanceWithDictionary SourceType Expr
-- ^ This is an instance derived via another type, desugared to include a
-- dictionary for the `via` type.
| ExplicitInstance [Declaration]
-- ^ This is a regular (explicit) instance
deriving (Show)

pattern DerivedInstanceWithDictionary :: Expr -> TypeInstanceBody
pattern DerivedInstanceWithDictionary dict <- (derivedInstanceDict -> Just dict)

derivedInstanceDict :: TypeInstanceBody -> Maybe Expr
derivedInstanceDict = \case
NewtypeInstanceWithDictionary dict -> Just dict
ViaInstanceWithDictionary _ dict -> Just dict
_ -> Nothing

mapTypeInstanceBody :: ([Declaration] -> [Declaration]) -> TypeInstanceBody -> TypeInstanceBody
mapTypeInstanceBody f = runIdentity . traverseTypeInstanceBody (Identity . f)

Expand Down
11 changes: 7 additions & 4 deletions lib/purescript-cst/src/Language/PureScript/CST/Convert.hs
Original file line number Diff line number Diff line change
Expand Up @@ -479,12 +479,15 @@ convertDeclaration fileName decl = case decl of
(convertType fileName <$> args)
(AST.ExplicitInstance $ goInstanceBinding <$> maybe [] (NE.toList . snd) bd)
uncurry goInst <$> zip [0..] (toList insts)
DeclDerive _ _ new (InstanceHead _ name _ ctrs cls args) -> do
DeclDerive _ _ strat (InstanceHead _ name _ ctrs cls args) -> do
let
name' = ident $ nameValue name
instTy
| isJust new = AST.NewtypeInstance
| otherwise = AST.DerivedInstance
instTy = case strat of
Just DeriveNewtype{} ->
AST.DerivedInstance (Just AST.DeriveNewtype)
Just (DeriveVia _ _ viaTy) ->
AST.DerivedInstance (Just (AST.DeriveVia $ convertType fileName viaTy))
_ -> AST.DerivedInstance Nothing
pure $ AST.TypeInstanceDeclaration ann [name'] 0 name'
(convertConstraint fileName <$> maybe [] (toList . fst) ctrs)
(qualified cls)
Expand Down
12 changes: 10 additions & 2 deletions lib/purescript-cst/src/Language/PureScript/CST/Parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import qualified Language.PureScript.Roles as R
import Language.PureScript.PSString (PSString)
}

%expect 95
%expect 98

%name parseType type
%name parseExpr expr
Expand Down Expand Up @@ -123,6 +123,7 @@ import Language.PureScript.PSString (PSString)
'then' { SourceToken _ (TokLowerName [] "then") }
'true' { SourceToken _ (TokLowerName [] "true") }
'type' { SourceToken _ (TokLowerName [] "type") }
'via' { SourceToken _ (TokLowerName [] "via") }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a contextual keyword, you will need to make sure to add it ident, qualIdent, and label.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Sorry about that, I added a few test cases to cover this.

'where' { SourceToken _ (TokLowerName [] "where") }
'(->)' { SourceToken _ (TokSymbolArr _) }
'(..)' { SourceToken _ (TokSymbolName [] "..") }
Expand Down Expand Up @@ -197,6 +198,7 @@ qualIdent :: { QualifiedName Ident }
| 'nominal' {% toQualifiedName Ident $1 }
| 'representational' {% toQualifiedName Ident $1 }
| 'phantom' {% toQualifiedName Ident $1 }
| 'via' {% toQualifiedName Ident $1 }

ident :: { Name Ident }
: LOWER {% toName Ident $1 }
Expand All @@ -207,6 +209,7 @@ ident :: { Name Ident }
| 'nominal' {% toName Ident $1 }
| 'representational' {% toName Ident $1 }
| 'phantom' {% toName Ident $1 }
| 'via' {% toName Ident $1 }

qualOp :: { QualifiedOpName }
: OPERATOR {% qualifiedOpName <\$> toQualifiedName N.OpName $1 }
Expand Down Expand Up @@ -267,6 +270,7 @@ label :: { Label }
| 'then' { toLabel $1 }
| 'true' { toLabel $1 }
| 'type' { toLabel $1 }
| 'via' { toLabel $1 }
| 'where' { toLabel $1 }

hole :: { Name Ident }
Expand Down Expand Up @@ -672,7 +676,7 @@ decl :: { Declaration () }
| 'newtype' properName '::' type {% checkNoWildcards $4 *> pure (DeclKindSignature () $1 (Labeled (getProperName $2) $3 $4)) }
| 'type' properName '::' type {% checkNoWildcards $4 *> pure (DeclKindSignature () $1 (Labeled (getProperName $2) $3 $4)) }
| 'derive' instHead { DeclDerive () $1 Nothing $2 }
| 'derive' 'newtype' instHead { DeclDerive () $1 (Just $2) $3 }
| 'derive' derivingStrategy instHead { DeclDerive () $1 (Just $2) $3 }
| ident '::' type { DeclSignature () (Labeled $1 $2 $3) }
| ident manyOrEmpty(binderAtom) guardedDecl { DeclValue () (ValueBindingFields $1 $2 $3) }
| fixity { DeclFixity () $1 }
Expand Down Expand Up @@ -735,6 +739,10 @@ fundep :: { ClassFundep }
classMember :: { Labeled (Name Ident) (Type ()) }
: ident '::' type {% checkNoWildcards $3 *> pure (Labeled $1 $2 $3) }

derivingStrategy :: { DerivingStrategy () }
: 'newtype' { DeriveNewtype () $1 }
| 'via' typeAtom { DeriveVia () $1 $2 }

instHead :: { InstanceHead () }
: 'instance' ident '::' constraints '=>' qualProperName manyOrEmpty(typeAtom)
{ InstanceHead $1 $2 $3 (Just ($4, $5)) (getQualifiedProperName $6) $7 }
Expand Down
7 changes: 6 additions & 1 deletion lib/purescript-cst/src/Language/PureScript/CST/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ data Declaration a
| DeclNewtype a (DataHead a) SourceToken (Name (N.ProperName 'N.ConstructorName)) (Type a)
| DeclClass a (ClassHead a) (Maybe (SourceToken, NonEmpty (Labeled (Name Ident) (Type a))))
| DeclInstanceChain a (Separated (Instance a))
| DeclDerive a SourceToken (Maybe SourceToken) (InstanceHead a)
| DeclDerive a SourceToken (Maybe (DerivingStrategy a)) (InstanceHead a)
| DeclKindSignature a SourceToken (Labeled (Name (N.ProperName 'N.TypeName)) (Type a))
| DeclSignature a (Labeled (Name Ident) (Type a))
| DeclValue a (ValueBindingFields a)
Expand Down Expand Up @@ -259,6 +259,11 @@ data ClassFundep
| FundepDetermines (NonEmpty (Name Ident)) SourceToken (NonEmpty (Name Ident))
deriving (Show, Eq, Ord, Generic)

data DerivingStrategy a
= DeriveNewtype a SourceToken
| DeriveVia a SourceToken (Type a)
deriving (Show, Eq, Ord, Functor, Foldable, Traversable)

data InstanceHead a = InstanceHead
{ instKeyword :: SourceToken
, instName :: Name Ident
Expand Down
132 changes: 121 additions & 11 deletions src/Language/PureScript/Errors.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Data.Foldable (fold)
import Data.Functor.Identity (Identity(..))
import Data.List (transpose, nubBy, partition, dropWhileEnd, sort, sortBy)
import qualified Data.List.NonEmpty as NEL
import Data.List.NonEmpty (NonEmpty((:|)))
import Data.Maybe (maybeToList, fromMaybe, mapMaybe)
import qualified Data.Map as M
import Data.Ord (comparing)
Expand Down Expand Up @@ -106,12 +107,17 @@ data SimpleErrorMessage
| UnknownClass (Qualified (ProperName 'ClassName))
| PossiblyInfiniteInstance (Qualified (ProperName 'ClassName)) [SourceType]
| CannotDerive (Qualified (ProperName 'ClassName)) [SourceType]
| CannotDeriveNullaryTypeClassInstance DerivingStrategy (Qualified (ProperName 'ClassName))
| InvalidDerivedInstance (Qualified (ProperName 'ClassName)) [SourceType] Int
| ExpectedTypeConstructor (Qualified (ProperName 'ClassName)) [SourceType] SourceType
| InvalidNewtypeInstance (Qualified (ProperName 'ClassName)) [SourceType]
| MissingNewtypeSuperclassInstance (Qualified (ProperName 'ClassName)) (Qualified (ProperName 'ClassName)) [SourceType]
| UnverifiableSuperclassInstance (Qualified (ProperName 'ClassName)) (Qualified (ProperName 'ClassName)) [SourceType]
| InvalidViaType (Qualified (ProperName 'ClassName)) [SourceType] SourceType
| InvalidViaKind (Qualified (ProperName 'ClassName)) [SourceType] SourceType SourceType SourceType
| FloatingViaTypeVariables (Qualified (ProperName 'ClassName)) [SourceType] SourceType (NEL.NonEmpty Text)
| MissingSuperclassInstance DerivingStrategy (Qualified (ProperName 'ClassName)) (Qualified (ProperName 'ClassName)) [SourceType]
| UnverifiableSuperclassInstance DerivingStrategy (Qualified (ProperName 'ClassName)) (Qualified (ProperName 'ClassName)) [SourceType]
| CannotFindDerivingType (ProperName 'TypeName)
| CannotFindViaType (ProperName 'TypeName)
| DuplicateLabel Label (Maybe Expr)
| DuplicateValueDeclaration Ident
| ArgListLengthsDiffer Ident
Expand Down Expand Up @@ -265,12 +271,17 @@ errorCode em = case unwrapErrorMessage em of
UnknownClass{} -> "UnknownClass"
PossiblyInfiniteInstance{} -> "PossiblyInfiniteInstance"
CannotDerive{} -> "CannotDerive"
CannotDeriveNullaryTypeClassInstance{} -> "CannotDeriveNullaryTypeClassInstance"
InvalidNewtypeInstance{} -> "InvalidNewtypeInstance"
MissingNewtypeSuperclassInstance{} -> "MissingNewtypeSuperclassInstance"
InvalidViaType{} -> "InvalidViaType"
InvalidViaKind{} -> "InvalidViaKind"
FloatingViaTypeVariables{} -> "FloatingViaTypeVariables"
MissingSuperclassInstance{} -> "MissingSuperclassInstance"
UnverifiableSuperclassInstance{} -> "UnverifiableSuperclassInstance"
InvalidDerivedInstance{} -> "InvalidDerivedInstance"
ExpectedTypeConstructor{} -> "ExpectedTypeConstructor"
CannotFindDerivingType{} -> "CannotFindDerivingType"
CannotFindViaType{} -> "CannotFindViaType"
DuplicateLabel{} -> "DuplicateLabel"
DuplicateValueDeclaration{} -> "DuplicateValueDeclaration"
ArgListLengthsDiffer{} -> "ArgListLengthsDiffer"
Expand Down Expand Up @@ -434,9 +445,13 @@ onTypesInErrorMessageM f (ErrorMessage hints simple) = ErrorMessage <$> traverse
gSimple (OverlappingInstances cl ts insts) = OverlappingInstances cl <$> traverse f ts <*> pure insts
gSimple (PossiblyInfiniteInstance cl ts) = PossiblyInfiniteInstance cl <$> traverse f ts
gSimple (CannotDerive cl ts) = CannotDerive cl <$> traverse f ts
gSimple (CannotDeriveNullaryTypeClassInstance strat cl) = pure $ CannotDeriveNullaryTypeClassInstance strat cl
gSimple (InvalidNewtypeInstance cl ts) = InvalidNewtypeInstance cl <$> traverse f ts
gSimple (MissingNewtypeSuperclassInstance cl1 cl2 ts) = MissingNewtypeSuperclassInstance cl1 cl2 <$> traverse f ts
gSimple (UnverifiableSuperclassInstance cl1 cl2 ts) = UnverifiableSuperclassInstance cl1 cl2 <$> traverse f ts
gSimple (InvalidViaType cl ts viaTy) = InvalidViaType cl <$> traverse f ts <*> f viaTy
gSimple (InvalidViaKind cl ts viaTy viaKind expectedKind) = InvalidViaKind cl <$> traverse f ts <*> f viaTy <*> f viaKind <*> f expectedKind
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should f be called on kinds here? There’s no case for the KindsDoNotUnify error for instance so maybe it shouldn’t 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are cases that are missing it's probably an oversight.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following SimpleErrorMessage constructors hold SourceTypes but aren’t matched:

  • InfiniteKind
  • EscapedSkolem
  • KindsDoNotUnify
  • MissingClassMember
  • UserDefinedWarning
  • QuantificationCheckFailureInType
  • UnsupportedTypeInKind

Should I add cases for them in this PR? Matching every constructor explicitly would be verbose but could help to prevent this in the future.

gSimple (FloatingViaTypeVariables cl ts viaTy viaTyVars) = FloatingViaTypeVariables cl <$> traverse f ts <*> f viaTy <*> pure viaTyVars
gSimple (MissingSuperclassInstance strat cl1 cl2 ts) = MissingSuperclassInstance strat cl1 cl2 <$> traverse f ts
gSimple (UnverifiableSuperclassInstance strat cl1 cl2 ts) = UnverifiableSuperclassInstance strat cl1 cl2 <$> traverse f ts
gSimple (InvalidDerivedInstance cl ts n) = InvalidDerivedInstance cl <$> traverse f ts <*> pure n
gSimple (ExpectedTypeConstructor cl ts ty) = ExpectedTypeConstructor cl <$> traverse f ts <*> f ty
gSimple (ExpectedType ty k) = ExpectedType <$> f ty <*> pure k
Expand Down Expand Up @@ -891,6 +906,16 @@ prettyPrintSingleError (PPEOptions codeColor full level showDocs relPath) e = fl
]
, line "since instances of this type class are not derivable."
]
renderSimpleErrorMessage (CannotDeriveNullaryTypeClassInstance DeriveNewtype nm) =
paras [ line $ "Cannot derive newtype instance for"
, markCodeBox $ indent $ line (showQualified runProperName nm)
, line "Nullary type classes cannot be derived."
]
renderSimpleErrorMessage (CannotDeriveNullaryTypeClassInstance (DeriveVia viaTy) nm) =
paras [ line $ "Cannot derive instance via " <> markCode (T.pack $ prettyPrintTypeSimplifiedInline viaTy) <> " for"
, markCodeBox $ indent $ line (showQualified runProperName nm)
, line "Nullary type classes cannot be derived."
]
renderSimpleErrorMessage (InvalidNewtypeInstance nm ts) =
paras [ line "Cannot derive newtype instance for"
, markCodeBox $ indent $ Box.hsep 1 Box.left
Expand All @@ -899,21 +924,94 @@ prettyPrintSingleError (PPEOptions codeColor full level showDocs relPath) e = fl
]
, line "Make sure this is a newtype."
]
renderSimpleErrorMessage (MissingNewtypeSuperclassInstance su cl ts) =
paras [ line "The derived newtype instance for"
renderSimpleErrorMessage (InvalidViaType nm ts viaTy) =
paras [ line $ "Cannot derive instance via " <> markCode (T.pack $ prettyPrintTypeSimplifiedInline viaTy) <> " for"
, markCodeBox $ indent $ Box.hsep 1 Box.left
[ line (showQualified runProperName nm)
, Box.vcat Box.left (map (typeAtomAsBox prettyDepth) ts)
]
, line $ T.intercalate " "
[ "because the type"
, markCode . T.pack $ prettyPrintTypeSimplifiedInline viaTy
, "is not of the required form T a_1 ... a_n."
]
]
renderSimpleErrorMessage (InvalidViaKind nm ts viaTy viaKind expectedKind) =
paras [ line $ "Cannot derive instance via " <> markCode (T.pack $ prettyPrintTypeSimplifiedInline viaTy) <> " for"
, markCodeBox $ indent $ Box.hsep 1 Box.left
[ line (showQualified runProperName nm)
, Box.vcat Box.left (map (typeAtomAsBox prettyDepth) ts)
]
, line $ T.intercalate " "
[ "because"
, markCode . T.pack $ prettyPrintTypeSimplifiedInline (last ts)
, "has kind"
, markCode . T.pack $ prettyPrintTypeSimplifiedInline expectedKind
, "but"
, markCode . T.pack $ prettyPrintTypeSimplifiedInline viaTy
, "has kind"
, markCode (T.pack $ prettyPrintTypeSimplifiedInline viaKind) <> "."
]
]
renderSimpleErrorMessage (FloatingViaTypeVariables nm ts viaTy viaTyVars) =
paras [ line $ "Cannot derive instance via " <> markCode (T.pack $ prettyPrintTypeSimplifiedInline viaTy) <> " for"
, markCodeBox $ indent $ Box.hsep 1 Box.left
[ line (showQualified runProperName nm)
, Box.vcat Box.left (map (typeAtomAsBox prettyDepth) ts)
]
, line $ T.intercalate " " $
"because" : case viaTyVars of
tv :| [] ->
[ "the type variable"
, markCode tv
, "is"
]
_ ->
[ "type variables"
, T.intercalate ", " $
markCode <$> NEL.init viaTyVars
, "and"
, markCode $ NEL.last viaTyVars
, "are"
]
++ [ "bound in"
, markCode . T.pack $ prettyPrintTypeSimplifiedInline viaTy
, "but"
, if length viaTyVars == 1 then "is" else "are"
, "not mentioned in the instance head."
]
]
renderSimpleErrorMessage (MissingSuperclassInstance DeriveNewtype su cl ts) =
paras [ line $ "The derived newtype instance for"
, markCodeBox $ indent $ Box.hsep 1 Box.left
[ line (showQualified runProperName cl)
, Box.vcat Box.left (map (typeAtomAsBox prettyDepth) ts)
]
, line $ "implies a missing superclass instance for " <> markCode (showQualified runProperName su) <> "."
]
renderSimpleErrorMessage (MissingSuperclassInstance (DeriveVia viaTy) su cl ts) =
paras [ line $ "The derived instance via " <> markCode (T.pack $ prettyPrintTypeSimplifiedInline viaTy) <> " for"
, markCodeBox $ indent $ Box.hsep 1 Box.left
[ line (showQualified runProperName cl)
, Box.vcat Box.left (map (typeAtomAsBox prettyDepth) ts)
]
, line $ "implies a missing superclass instance for " <> markCode (showQualified runProperName su) <> "."
]
renderSimpleErrorMessage (UnverifiableSuperclassInstance DeriveNewtype su cl ts) =
paras [ line $ "The derived newtype instance for"
, markCodeBox $ indent $ Box.hsep 1 Box.left
[ line (showQualified runProperName cl)
, Box.vcat Box.left (map (typeAtomAsBox prettyDepth) ts)
]
, line $ "does not include a derived superclass instance for " <> markCode (showQualified runProperName su) <> "."
, line $ "implies a superclass instance for " <> markCode (showQualified runProperName su) <> " which could not be verified."
]
renderSimpleErrorMessage (UnverifiableSuperclassInstance su cl ts) =
paras [ line "The derived newtype instance for"
renderSimpleErrorMessage (UnverifiableSuperclassInstance (DeriveVia viaTy) su cl ts) =
paras [ line $ "The derived instance via " <> markCode (T.pack $ prettyPrintTypeSimplifiedInline viaTy) <> " for"
, markCodeBox $ indent $ Box.hsep 1 Box.left
[ line (showQualified runProperName cl)
, Box.vcat Box.left (map (typeAtomAsBox prettyDepth) ts)
]
, line $ "implies an superclass instance for " <> markCode (showQualified runProperName su) <> " which could not be verified."
, line $ "implies a superclass instance for " <> markCode (showQualified runProperName su) <> " which could not be verified."
]
renderSimpleErrorMessage (InvalidDerivedInstance nm ts argCount) =
paras [ line "Cannot derive the type class instance"
Expand Down Expand Up @@ -943,6 +1041,8 @@ prettyPrintSingleError (PPEOptions codeColor full level showDocs relPath) e = fl
]
renderSimpleErrorMessage (CannotFindDerivingType nm) =
line $ "Cannot derive a type class instance, because the type declaration for " <> markCode (runProperName nm) <> " could not be found."
renderSimpleErrorMessage (CannotFindViaType nm) =
line $ "Cannot derive a type class instance via " <> markCode (runProperName nm) <> " because its type declaration could not be found."
renderSimpleErrorMessage (DuplicateLabel l expr) =
paras $ [ line $ "Label " <> markCode (prettyPrintLabel l) <> " appears more than once in a row type." ]
<> foldMap (\expr' -> [ line "Relevant expression: "
Expand Down Expand Up @@ -1547,8 +1647,14 @@ prettyPrintSingleError (PPEOptions codeColor full level showDocs relPath) e = fl
where
isSolverHint ErrorSolvingConstraint{} = True
isSolverHint _ = False
stripRedundantHints InvalidViaType{} = stripFirst isInstanceHint
stripRedundantHints InvalidViaKind{} = stripFirst isInstanceHint
stripRedundantHints FloatingViaTypeVariables{} = stripFirst isInstanceHint
stripRedundantHints _ = id

isInstanceHint ErrorInInstance{} = True
isInstanceHint _ = False

stripFirst :: (ErrorMessageHint -> Bool) -> [ErrorMessageHint] -> [ErrorMessageHint]
stripFirst p (PositionedError pos : hs) = PositionedError pos : stripFirst p hs
stripFirst p (ErrorInModule mn : hs) = ErrorInModule mn : stripFirst p hs
Expand Down Expand Up @@ -1613,6 +1719,10 @@ prettyPrintKindSignatureFor NewtypeSig = "newtype"
prettyPrintKindSignatureFor TypeSynonymSig = "type"
prettyPrintKindSignatureFor ClassSig = "class"

prettyPrintTypeSimplifiedInline :: Type a -> String
prettyPrintTypeSimplifiedInline =
unwords . lines . prettyPrintType maxBound . eraseForAllKindAnnotations . eraseKindApps

prettyPrintSuggestedTypeSimplified :: Type a -> String
prettyPrintSuggestedTypeSimplified = prettyPrintSuggestedType . eraseForAllKindAnnotations . eraseKindApps

Expand Down
Loading








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/purescript/purescript/pull/3824/files

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy