{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeOperators #-} module Axel.Haskell.Converter where import Prelude hiding (putStrLn) import qualified Axel.AST as AST import Axel.Denormalize (denormalizeStatement) import Axel.Eff.Console (putStrLn) import qualified Axel.Eff.Console as Effs (Console) import qualified Axel.Eff.FileSystem as Effs (FileSystem) import qualified Axel.Eff.FileSystem as FS (writeFile) import Axel.Parse.AST (toAxel) import Axel.Utils.String (replace) import Control.Lens.Operators ((<&>)) import Control.Monad.Freer (Eff, LastMember, Members, sendM) import qualified Language.Haskell.Exts as HSE renderRaw :: (HSE.Pretty a) => a -> String renderRaw = escapeNewlines . escapeQuotes . HSE.prettyPrintWithMode ppMode where ppMode = HSE.defaultMode {HSE.layout = HSE.PPNoLayout} escapeQuotes = replace "\"" "\\\"" escapeNewlines = replace "\n" "\\n" unsupportedExpr :: (HSE.Pretty a) => a -> AST.Expression unsupportedExpr = AST.ERawExpression . renderRaw unsupportedStmt :: (HSE.Pretty a) => a -> AST.Statement unsupportedStmt = AST.SRawStatement . renderRaw class ToExpr a where toExpr :: a b -> AST.Expression class ToStmts a where toStmts :: a b -> [AST.Statement] toId :: (ToExpr a) => a b -> String toId x = let AST.EIdentifier sym = toExpr x in sym instance ToExpr HSE.Name where toExpr (HSE.Ident _ name) = AST.EIdentifier name toExpr (HSE.Symbol _ name) = AST.EIdentifier name instance ToExpr HSE.ModuleName where toExpr (HSE.ModuleName _ name) = AST.EIdentifier name instance ToExpr HSE.CName where toExpr (HSE.VarName _ name) = toExpr name toExpr (HSE.ConName _ name) = toExpr name instance ToExpr HSE.SpecialCon where toExpr HSE.UnitCon {} = AST.EIdentifier "Unit" toExpr HSE.ListCon {} = AST.EIdentifier "List" toExpr HSE.FunCon {} = AST.EIdentifier "->" toExpr (HSE.TupleCon _ _ arity) = AST.EIdentifier $ replicate arity ',' toExpr HSE.Cons {} = AST.EIdentifier ":" toExpr expr@HSE.UnboxedSingleCon {} = unsupportedExpr expr toExpr HSE.ExprHole {} = AST.EIdentifier "_" instance ToExpr HSE.QName where toExpr (HSE.Qual _ moduleName name) = AST.EIdentifier $ toId moduleName <> "." <> toId name toExpr (HSE.UnQual _ name) = toExpr name toExpr (HSE.Special _ specialCon) = toExpr specialCon instance ToExpr HSE.TyVarBind where toExpr (HSE.UnkindedVar _ name) = toExpr name toExpr expr@HSE.KindedVar {} = unsupportedExpr expr instance ToExpr HSE.MaybePromotedName where toExpr expr@HSE.PromotedName {} = unsupportedExpr expr toExpr (HSE.UnpromotedName _ name) = toExpr name instance ToExpr HSE.Promoted where toExpr expr@HSE.PromotedInteger {} = unsupportedExpr expr toExpr expr@HSE.PromotedString {} = unsupportedExpr expr toExpr (HSE.PromotedCon _ _ con) = AST.EIdentifier $ '\'' : toId con toExpr (HSE.PromotedList _ _ list) = AST.EFunctionApplication $ AST.FunctionApplication (AST.EIdentifier "list") (map toExpr list) toExpr expr@HSE.PromotedTuple {} = unsupportedExpr expr toExpr expr@HSE.PromotedUnit {} = unsupportedExpr expr instance ToExpr HSE.Type where toExpr expr@HSE.TyForall {} = unsupportedExpr expr toExpr (HSE.TyFun _ tyA tyB) = AST.EFunctionApplication $ AST.FunctionApplication (AST.EIdentifier "->") [toExpr tyA, toExpr tyB] toExpr (HSE.TyTuple _ _ tys) = AST.EFunctionApplication $ AST.FunctionApplication (AST.EIdentifier ",") (map toExpr tys) toExpr expr@HSE.TyUnboxedSum {} = unsupportedExpr expr toExpr (HSE.TyList _ ty) = AST.EFunctionApplication $ AST.FunctionApplication (AST.EIdentifier "[]") [toExpr ty] toExpr expr@HSE.TyParArray {} = unsupportedExpr expr toExpr (HSE.TyApp _ tyA tyB) = AST.EFunctionApplication $ AST.FunctionApplication (toExpr tyA) [toExpr tyB] toExpr (HSE.TyVar _ x) = toExpr x toExpr (HSE.TyCon _ x) = toExpr x toExpr (HSE.TyParen _ x) = toExpr x toExpr (HSE.TyInfix _ tyA mpn tyB) = AST.EFunctionApplication $ AST.FunctionApplication (toExpr mpn) [toExpr tyA, toExpr tyB] toExpr expr@HSE.TyKind {} = unsupportedExpr expr toExpr (HSE.TyPromoted _ promoted) = toExpr promoted toExpr expr@HSE.TyEquals {} = unsupportedExpr expr toExpr expr@HSE.TySplice {} = unsupportedExpr expr toExpr expr@HSE.TyBang {} = unsupportedExpr expr toExpr (HSE.TyWildCard _ name) = AST.EIdentifier $ "_" <> maybe mempty toId name toExpr expr@HSE.TyQuasiQuote {} = unsupportedExpr expr instance ToExpr HSE.ModuleHead where toExpr (HSE.ModuleHead _ name _ _) = toExpr name instance ToStmts HSE.ModulePragma where toStmts (HSE.LanguagePragma _ pragmas) = map (\pragma -> AST.SPragma . AST.Pragma $ "LANGUAGE " <> toId pragma) pragmas toStmts stmt@HSE.OptionsPragma {} = [unsupportedStmt stmt] toStmts stmt@HSE.AnnModulePragma {} = [unsupportedStmt stmt] instance ToStmts HSE.ImportDecl where toStmts stmt@(HSE.ImportDecl _ _ _ _ _ _ _ (Just (HSE.ImportSpecList _ True _))) = [unsupportedStmt stmt] toStmts stmt@(HSE.ImportDecl _ moduleName isQualified _ _ _ alias spec) = [ let moduleId = toId moduleName in if isQualified then case alias of Just aliasName -> AST.SQualifiedImport $ AST.QualifiedImport moduleId (toId aliasName) (importSpecListToExpr spec) Nothing -> unsupportedStmt stmt else case alias of Nothing -> case importSpecListToExpr spec of AST.ImportAll -> AST.SUnrestrictedImport moduleId AST.ImportOnly imports -> AST.SRestrictedImport $ AST.RestrictedImport moduleId (AST.ImportOnly imports) Just _ -> unsupportedStmt stmt ] where importSpecListToExpr Nothing = AST.ImportAll importSpecListToExpr (Just (HSE.ImportSpecList _ False importSpecs)) = AST.ImportOnly $ map (\case HSE.IVar _ name -> AST.ImportItem (toId name) HSE.IAbs _ _ name -> AST.ImportItem (toId name) HSE.IThingAll _ name -> AST.ImportType (toId name) [".."] HSE.IThingWith _ name items -> AST.ImportType (toId name) (map toId items)) importSpecs instance ToStmts HSE.Module where toStmts (HSE.Module _ moduleHead pragmas imports decls) = concat [ concatMap toStmts pragmas , case moduleHead of Just moduleId -> [AST.SModuleDeclaration (toId moduleId)] Nothing -> [] , concatMap toStmts imports , concatMap toStmts decls ] instance ToExpr HSE.QualConDecl where toExpr (HSE.QualConDecl _ _ _ conDecl) = case conDecl of HSE.ConDecl _ name args -> AST.EFunctionApplication $ AST.FunctionApplication (AST.EIdentifier $ toId name) (map toExpr args) HSE.InfixConDecl _ argA name argB -> AST.EFunctionApplication $ AST.FunctionApplication (AST.EIdentifier $ toId name) [toExpr argA, toExpr argB] HSE.RecDecl {} -> unsupportedExpr conDecl instance ToExpr HSE.Literal where toExpr (HSE.Char _ char _) = AST.ELiteral $ AST.LChar char toExpr (HSE.String _ string _) = AST.ELiteral $ AST.LString string toExpr (HSE.Int _ int _) = AST.ELiteral $ AST.LInt (fromInteger int) toExpr expr@HSE.Frac {} = unsupportedExpr expr toExpr expr@HSE.PrimInt {} = unsupportedExpr expr toExpr expr@HSE.PrimWord {} = unsupportedExpr expr toExpr expr@HSE.PrimFloat {} = unsupportedExpr expr toExpr expr@HSE.PrimDouble {} = unsupportedExpr expr toExpr expr@HSE.PrimChar {} = unsupportedExpr expr toExpr expr@HSE.PrimString {} = unsupportedExpr expr instance ToExpr HSE.Pat where toExpr (HSE.PVar _ name) = toExpr name toExpr (HSE.PLit _ _ literal) = toExpr literal toExpr expr@HSE.PNPlusK {} = unsupportedExpr expr toExpr (HSE.PInfixApp _ patA name patB) = AST.EFunctionApplication $ AST.FunctionApplication (toExpr name) [toExpr patA, toExpr patB] toExpr (HSE.PApp _ name pats) = AST.EFunctionApplication $ AST.FunctionApplication (toExpr name) (map toExpr pats) toExpr (HSE.PTuple _ _ pats) = AST.EFunctionApplication $ AST.FunctionApplication (AST.EIdentifier ",") (map toExpr pats) toExpr expr@HSE.PUnboxedSum {} = unsupportedExpr expr toExpr (HSE.PList _ pats) = AST.EFunctionApplication $ AST.FunctionApplication (AST.EIdentifier "list") (map toExpr pats) toExpr (HSE.PParen _ pat) = toExpr pat toExpr expr@HSE.PRec {} = unsupportedExpr expr toExpr expr@HSE.PAsPat {} = unsupportedExpr expr toExpr HSE.PWildCard {} = AST.EIdentifier "_" toExpr expr@HSE.PIrrPat {} = unsupportedExpr expr toExpr expr@HSE.PatTypeSig {} = unsupportedExpr expr toExpr expr@HSE.PViewPat {} = unsupportedExpr expr toExpr expr@HSE.PRPat {} = unsupportedExpr expr toExpr expr@HSE.PXTag {} = unsupportedExpr expr toExpr expr@HSE.PXETag {} = unsupportedExpr expr toExpr expr@HSE.PXPcdata {} = unsupportedExpr expr toExpr expr@HSE.PXPatTag {} = unsupportedExpr expr toExpr expr@HSE.PXRPats {} = unsupportedExpr expr toExpr expr@HSE.PSplice {} = unsupportedExpr expr toExpr expr@HSE.PQuasiQuote {} = unsupportedExpr expr toExpr expr@HSE.PBangPat {} = unsupportedExpr expr declHeadToTyDef :: HSE.DeclHead a -> AST.TypeDefinition declHeadToTyDef (HSE.DHead _ name) = AST.ProperType $ toId name declHeadToTyDef HSE.DHInfix {} = error "Postfix type declarations not supported!" declHeadToTyDef (HSE.DHParen _ dh) = declHeadToTyDef dh declHeadToTyDef (HSE.DHApp _ dh tvb) = AST.TypeConstructor $ case dh of HSE.DHInfix _ tvb' name -> AST.FunctionApplication (toExpr name) [toExpr tvb', toExpr tvb] _ -> AST.FunctionApplication (tyDefToExpr $ declHeadToTyDef dh) [toExpr tvb] tyDefToExpr :: AST.TypeDefinition -> AST.Expression tyDefToExpr (AST.TypeConstructor tyCon) = AST.EFunctionApplication tyCon tyDefToExpr (AST.ProperType ty) = AST.EIdentifier ty exprToTyDef :: AST.Expression -> AST.TypeDefinition exprToTyDef (AST.EIdentifier identifier) = AST.ProperType identifier exprToTyDef (AST.EFunctionApplication funApp) = AST.TypeConstructor funApp toFunApp :: AST.Expression -> AST.FunctionApplication toFunApp (AST.EFunctionApplication funApp) = funApp toFunApp (AST.EIdentifier sym) = AST.FunctionApplication (AST.EIdentifier sym) [] bindsToFunDefs :: Maybe (HSE.Binds a) -> [AST.FunctionDefinition] bindsToFunDefs Nothing = [] bindsToFunDefs (Just (HSE.BDecls _ decls)) = map (\decl -> case toStmts decl of [AST.SFunctionDefinition funDef] -> funDef) decls bindsToFunDefs (Just HSE.IPBinds {}) = error "Implicit parameters not supported!" altToClause :: HSE.Alt a -> (AST.Expression, AST.Expression) altToClause (HSE.Alt _ pat rhs _) = (toExpr pat, toExpr rhs) instance ToExpr HSE.QOp where toExpr (HSE.QVarOp _ name) = toExpr name toExpr (HSE.QConOp _ name) = toExpr name instance ToExpr HSE.Exp where toExpr (HSE.Var _ name) = toExpr name toExpr expr@HSE.OverloadedLabel {} = unsupportedExpr expr toExpr expr@HSE.IPVar {} = unsupportedExpr expr toExpr (HSE.Con _ name) = toExpr name toExpr (HSE.Lit _ lit) = toExpr lit toExpr (HSE.InfixApp _ a op b) = AST.EFunctionApplication $ AST.FunctionApplication (AST.EIdentifier "infixApply") [toExpr a, toExpr op, toExpr b] toExpr (HSE.App _ f x) = AST.EFunctionApplication $ AST.FunctionApplication (toExpr f) [toExpr x] toExpr expr@HSE.NegApp {} = unsupportedExpr expr toExpr (HSE.Lambda _ args body) = AST.ELambda $ AST.Lambda (map toExpr args) (toExpr body) toExpr (HSE.Let _ binds body) = AST.ELetBlock $ AST.LetBlock (bindsToClauses binds) (toExpr body) toExpr (HSE.If _ cond ifTrue ifFalse) = AST.EIfBlock $ AST.IfBlock (toExpr cond) (toExpr ifTrue) (toExpr ifFalse) toExpr expr@HSE.MultiIf {} = unsupportedExpr expr toExpr (HSE.Case _ expr matches) = AST.ECaseBlock $ AST.CaseBlock (toExpr expr) (map altToClause matches) toExpr expr@HSE.Do {} = unsupportedExpr expr toExpr expr@HSE.MDo {} = unsupportedExpr expr toExpr (HSE.Tuple _ _ exps) = AST.EFunctionApplication $ AST.FunctionApplication (AST.EIdentifier $ replicate (length exps) ',') (map toExpr exps) toExpr expr@HSE.UnboxedSum {} = unsupportedExpr expr toExpr expr@HSE.TupleSection {} = unsupportedExpr expr toExpr (HSE.List _ items) = AST.EFunctionApplication $ AST.FunctionApplication (AST.EIdentifier "list") (map toExpr items) toExpr expr@HSE.ParArray {} = unsupportedExpr expr toExpr (HSE.Paren _ expr) = toExpr expr toExpr expr@HSE.LeftSection {} = unsupportedExpr expr toExpr expr@HSE.RightSection {} = unsupportedExpr expr toExpr expr@HSE.RecConstr {} = unsupportedExpr expr toExpr expr@HSE.RecUpdate {} = unsupportedExpr expr toExpr expr@HSE.EnumFrom {} = unsupportedExpr expr toExpr expr@HSE.EnumFromTo {} = unsupportedExpr expr toExpr expr@HSE.EnumFromThen {} = unsupportedExpr expr toExpr expr@HSE.EnumFromThenTo {} = unsupportedExpr expr toExpr expr@HSE.ListComp {} = unsupportedExpr expr toExpr expr@HSE.ParComp {} = unsupportedExpr expr toExpr expr@HSE.ParArrayComp {} = unsupportedExpr expr toExpr expr@HSE.ParArrayFromTo {} = unsupportedExpr expr toExpr expr@HSE.ParArrayFromThenTo {} = unsupportedExpr expr toExpr expr@HSE.ExpTypeSig {} = unsupportedExpr expr toExpr expr@HSE.VarQuote {} = unsupportedExpr expr toExpr expr@HSE.TypQuote {} = unsupportedExpr expr toExpr expr@HSE.BracketExp {} = unsupportedExpr expr toExpr expr@HSE.SpliceExp {} = unsupportedExpr expr toExpr expr@HSE.QuasiQuote {} = unsupportedExpr expr toExpr expr@HSE.TypeApp {} = unsupportedExpr expr toExpr expr@HSE.XTag {} = unsupportedExpr expr toExpr expr@HSE.XETag {} = unsupportedExpr expr toExpr expr@HSE.XPcdata {} = unsupportedExpr expr toExpr expr@HSE.XExpTag {} = unsupportedExpr expr toExpr expr@HSE.XChildTag {} = unsupportedExpr expr toExpr expr@HSE.CorePragma {} = unsupportedExpr expr toExpr expr@HSE.SCCPragma {} = unsupportedExpr expr toExpr expr@HSE.GenPragma {} = unsupportedExpr expr toExpr expr@HSE.Proc {} = unsupportedExpr expr toExpr expr@HSE.LeftArrApp {} = unsupportedExpr expr toExpr expr@HSE.RightArrApp {} = unsupportedExpr expr toExpr expr@HSE.LeftArrHighApp {} = unsupportedExpr expr toExpr expr@HSE.RightArrHighApp {} = unsupportedExpr expr toExpr expr@HSE.LCase {} = unsupportedExpr expr instance ToExpr HSE.Rhs where toExpr (HSE.UnGuardedRhs _ expr) = toExpr expr toExpr expr@HSE.GuardedRhss {} = unsupportedExpr expr bindsToClauses :: HSE.Binds a -> [(AST.Expression, AST.Expression)] bindsToClauses (HSE.BDecls _ decls) = map (\(HSE.PatBind _ pat body _) -> (toExpr pat, toExpr body)) decls instance ToStmts HSE.Match where toStmts (HSE.Match _ fn pats body whereBinds) = [ AST.SFunctionDefinition $ AST.FunctionDefinition (toId fn) (map toExpr pats) (toExpr body) (bindsToFunDefs whereBinds) ] toStmts (HSE.InfixMatch _ pat fn pats body whereBinds) = [ AST.SFunctionDefinition $ AST.FunctionDefinition (toId fn) (toExpr pat : map toExpr pats) (toExpr body) (bindsToFunDefs whereBinds) ] instance ToExpr HSE.InstHead where toExpr (HSE.IHCon _ name) = toExpr name toExpr (HSE.IHInfix _ ty name) = AST.EFunctionApplication $ AST.FunctionApplication (toExpr ty) [toExpr name] toExpr (HSE.IHParen _ instHead) = toExpr instHead toExpr (HSE.IHApp _ instHead ty) = AST.EFunctionApplication $ AST.FunctionApplication (toExpr instHead) [toExpr ty] instance ToExpr HSE.InstRule where toExpr (HSE.IRule _ _ Nothing instHead) = toExpr instHead toExpr (HSE.IParen _ rule) = toExpr rule instance ToStmts HSE.Decl where toStmts (HSE.TypeDecl _ declHead ty) = [ AST.STypeSynonym $ AST.TypeSynonym (tyDefToExpr $ declHeadToTyDef declHead) (toExpr ty) ] toStmts stmt@HSE.TypeFamDecl {} = [unsupportedStmt stmt] toStmts stmt@HSE.ClosedTypeFamDecl {} = [unsupportedStmt stmt] toStmts (HSE.DataDecl _ dataOrNew _ declHead cases _) = [ case dataOrNew of HSE.NewType _ -> AST.SNewtypeDeclaration $ AST.NewtypeDeclaration (declHeadToTyDef declHead) (toFunApp $ toExpr $ head cases) HSE.DataType _ -> AST.SDataDeclaration $ AST.DataDeclaration (declHeadToTyDef declHead) (map (toFunApp . toExpr) cases) ] toStmts stmt@HSE.GDataDecl {} = [unsupportedStmt stmt] toStmts stmt@HSE.DataFamDecl {} = [unsupportedStmt stmt] toStmts stmt@HSE.TypeInsDecl {} = [unsupportedStmt stmt] toStmts stmt@HSE.DataInsDecl {} = [unsupportedStmt stmt] toStmts stmt@HSE.GDataInsDecl {} = [unsupportedStmt stmt] toStmts (HSE.ClassDecl _ ctxt declHead _ decls) = [ AST.STypeclassDefinition $ AST.TypeclassDefinition (tyDefToExpr $ declHeadToTyDef declHead) (contextToExprs ctxt) (maybe [] (map classDeclToTySig) decls) ] toStmts (HSE.InstDecl _ _ rule decls) = [ AST.STypeclassInstance $ AST.TypeclassInstance (toExpr rule) (maybe [] (map instDeclToFunDef) decls) ] toStmts stmt@HSE.DerivDecl {} = [unsupportedStmt stmt] toStmts stmt@HSE.InfixDecl {} = [unsupportedStmt stmt] toStmts (HSE.TypeSig _ names ty) = map (\name -> AST.STypeSignature $ AST.TypeSignature (toId name) (toExpr ty)) names toStmts stmt@HSE.PatSynSig {} = [unsupportedStmt stmt] toStmts (HSE.FunBind _ cases) = concatMap toStmts cases toStmts (HSE.PatBind _ fn body whereBinds) = [ AST.SFunctionDefinition $ AST.FunctionDefinition (toId fn) [] (toExpr body) (bindsToFunDefs whereBinds) ] toStmts stmt@HSE.ForImp {} = [unsupportedStmt stmt] toStmts stmt@HSE.ForExp {} = [unsupportedStmt stmt] toStmts stmt@HSE.RulePragmaDecl {} = [unsupportedStmt stmt] toStmts stmt@HSE.DeprPragmaDecl {} = [unsupportedStmt stmt] toStmts stmt@HSE.WarnPragmaDecl {} = [unsupportedStmt stmt] toStmts stmt@HSE.InlineSig {} = [unsupportedStmt stmt] toStmts stmt@HSE.InlineConlikeSig {} = [unsupportedStmt stmt] toStmts stmt@HSE.SpecSig {} = [unsupportedStmt stmt] toStmts stmt@HSE.SpecInlineSig {} = [unsupportedStmt stmt] toStmts stmt@HSE.InstSig {} = [unsupportedStmt stmt] toStmts stmt@HSE.AnnPragma {} = [unsupportedStmt stmt] toStmts stmt@HSE.MinimalPragma {} = [unsupportedStmt stmt] toStmts stmt@HSE.RoleAnnotDecl {} = [unsupportedStmt stmt] toStmts stmt@HSE.CompletePragma {} = [unsupportedStmt stmt] instDeclToFunDef :: HSE.InstDecl a -> AST.FunctionDefinition instDeclToFunDef (HSE.InsDecl _ decl) = case head $ toStmts decl of AST.SFunctionDefinition funDef -> funDef instDeclToFunDef HSE.InsType {} = error "Type families not supported!" instDeclToFunDef HSE.InsData {} = error "Type families not supported!" instDeclToFunDef HSE.InsGData {} = error "Type families not supported!" classDeclToTySig :: HSE.ClassDecl a -> AST.TypeSignature classDeclToTySig (HSE.ClsDecl _ decl) = case toStmts decl of [AST.STypeSignature tySig] -> tySig classDeclToTySig HSE.ClsDataFam {} = error "Type families not supported!" classDeclToTySig HSE.ClsTyFam {} = error "Type families not supported!" classDeclToTySig HSE.ClsTyDef {} = error "Type families not supported!" classDeclToTySig HSE.ClsDefSig {} = error "Default signatures not supported!" instance ToExpr HSE.Asst where toExpr (HSE.ClassA _ name tys) = AST.EFunctionApplication $ AST.FunctionApplication (toExpr name) (map toExpr tys) toExpr (HSE.WildCardA _ _) = AST.EIdentifier "_" contextToExprs :: Maybe (HSE.Context a) -> [AST.Expression] contextToExprs Nothing = [] contextToExprs (Just (HSE.CxSingle _ asst)) = [toExpr asst] contextToExprs (Just (HSE.CxTuple _ assts)) = map toExpr assts contextToExprs (Just (HSE.CxEmpty _)) = [] convertFile :: (LastMember IO effs, Members '[ Effs.Console, Effs.FileSystem] effs) => FilePath -> FilePath -> Eff effs String convertFile path newPath = do newContents <- sendM (HSE.parseFile path) <&> (\case HSE.ParseOk parsedModule -> unlines $ map (toAxel . denormalizeStatement) $ toStmts parsedModule HSE.ParseFailed _ err -> error err) putStrLn $ "Writing " <> newPath <> "..." FS.writeFile newPath newContents pure newPath