diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+Haskell-Tools is Copyright (c) Boldizsár Németh, 2016, all rights reserved,
+and is distributed as free software under the following license.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions, and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions, and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- The names of the copyright holders may not be used to endorse or
+promote products derived from this software without specific prior
+written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Language/Haskell/Tools/Refactor/Builtin/DataToNewtype.hs b/Language/Haskell/Tools/Refactor/Builtin/DataToNewtype.hs
new file mode 100644
--- /dev/null
+++ b/Language/Haskell/Tools/Refactor/Builtin/DataToNewtype.hs
@@ -0,0 +1,15 @@
+module Language.Haskell.Tools.Refactor.Builtin.DataToNewtype (dataToNewtype, tryItOut) where
+
+import Control.Reference ((.=), (.-), (&))
+import Language.Haskell.Tools.Refactor
+
+tryItOut :: String -> IO ()
+tryItOut moduleName = tryRefactor (\_ -> localRefactoring dataToNewtype) moduleName ""
+
+dataToNewtype :: LocalRefactoring dom
+dataToNewtype = return . (modDecl & annList .- changeDeclaration)
+
+changeDeclaration :: Decl dom -> Decl dom
+changeDeclaration dd@(DataDecl DataKeyword _ _ (AnnList [ConDecl _ (AnnList [_])]) _)
+  = declNewtype .= mkNewtypeKeyword $ dd
+changeDeclaration decl = decl
diff --git a/Language/Haskell/Tools/Refactor/Builtin/DollarApp.hs b/Language/Haskell/Tools/Refactor/Builtin/DollarApp.hs
new file mode 100644
--- /dev/null
+++ b/Language/Haskell/Tools/Refactor/Builtin/DollarApp.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE FlexibleContexts, ConstraintKinds, TypeFamilies #-}
+module Language.Haskell.Tools.Refactor.Builtin.DollarApp (dollarApp, DollarDomain, tryItOut) where
+
+import Language.Haskell.Tools.Refactor
+
+import BasicTypes (Fixity(..))
+import Id (idName)
+import qualified Name as GHC (Name)
+import PrelInfo (wiredInIds)
+import PrelNames (dollarIdKey)
+import SrcLoc (RealSrcSpan, SrcSpan)
+import Unique (getUnique)
+
+import Control.Monad.State
+import Control.Reference ((^.), (!~), biplateRef)
+
+tryItOut :: String -> String -> IO ()
+tryItOut = tryRefactor (localRefactoring . dollarApp)
+
+type DollarMonad dom = StateT [SrcSpan] (LocalRefactor dom)
+type DollarDomain dom = (HasImportInfo dom, HasModuleInfo dom, HasFixityInfo dom, HasNameInfo dom)
+
+dollarApp :: DollarDomain dom => RealSrcSpan -> LocalRefactoring dom
+dollarApp sp = flip evalStateT [] . ((nodesContained sp !~ (\e -> get >>= replaceExpr e))
+                                        >=> (biplateRef !~ parenExpr))
+
+replaceExpr :: DollarDomain dom => Expr dom -> [SrcSpan] -> DollarMonad dom (Expr dom)
+replaceExpr expr@(App _ (Paren (InfixApp _ op arg))) replacedRanges
+  | not (getRange arg `elem` replacedRanges)
+  , semanticsName (op ^. operatorName) /= Just dollarName
+  , case semanticsFixity (op ^. operatorName) of Just (Fixity _ p _) | p > 0 -> False; _ -> True
+  = return expr
+replaceExpr (App fun (Paren arg)) _ = do modify $ (getRange arg :)
+                                         mkInfixApp fun <$> lift (referenceOperator dollarName) <*> pure arg
+replaceExpr e _ = return e
+
+parenExpr :: Expr dom -> DollarMonad dom (Expr dom)
+parenExpr e = (exprLhs !~ parenDollar True) =<< (exprRhs !~ parenDollar False $ e)
+
+parenDollar :: Bool -> Expr dom -> DollarMonad dom (Expr dom)
+parenDollar lhs expr@(InfixApp _ _ arg)
+  = do replacedRanges <- get
+       if getRange arg `elem` replacedRanges && (lhs || getRange expr `notElem` replacedRanges)
+         then return $ mkParen expr
+         else return expr
+parenDollar _ e = return e
+
+dollarName :: GHC.Name
+[dollarName] = map idName $ filter ((dollarIdKey==) . getUnique) wiredInIds
diff --git a/Language/Haskell/Tools/Refactor/Builtin/DollarApp1.hs b/Language/Haskell/Tools/Refactor/Builtin/DollarApp1.hs
new file mode 100644
--- /dev/null
+++ b/Language/Haskell/Tools/Refactor/Builtin/DollarApp1.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
+module Language.Haskell.Tools.Refactor.Builtin.DollarApp1 where
+
+import Language.Haskell.Tools.Refactor
+
+import Control.Reference ((.-))
+import SrcLoc (RealSrcSpan)
+
+tryItOut :: String -> String -> IO ()
+tryItOut = tryRefactor (localRefactoring . dollarApp)
+
+dollarApp :: Domain dom => RealSrcSpan -> LocalRefactoring dom
+dollarApp sp = return . (nodesContained sp .- replaceExpr)
+
+replaceExpr :: Expr dom -> Expr dom
+replaceExpr (App fun (Paren arg)) = mkInfixApp fun (mkUnqualOp "$") arg
+replaceExpr e = e
diff --git a/Language/Haskell/Tools/Refactor/Builtin/DollarApp2.hs b/Language/Haskell/Tools/Refactor/Builtin/DollarApp2.hs
new file mode 100644
--- /dev/null
+++ b/Language/Haskell/Tools/Refactor/Builtin/DollarApp2.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE FlexibleContexts, TypeFamilies #-}
+module Language.Haskell.Tools.Refactor.Builtin.DollarApp2 where
+
+import Language.Haskell.Tools.Refactor
+
+import Control.Reference ((!~))
+import Id (idName)
+import PrelInfo (wiredInIds)
+import PrelNames (dollarIdKey)
+import SrcLoc (RealSrcSpan)
+import Unique (getUnique)
+
+tryItOut :: String -> String -> IO ()
+tryItOut = tryRefactor (localRefactoring . dollarApp)
+
+dollarApp :: (HasImportInfo dom, HasModuleInfo dom) => RealSrcSpan -> LocalRefactoring dom
+dollarApp sp = nodesContained sp !~ replaceExpr
+
+replaceExpr :: (HasImportInfo dom, HasModuleInfo dom) => Expr dom -> LocalRefactor dom (Expr dom)
+replaceExpr (App fun (Paren arg)) = mkInfixApp fun <$> referenceOperator dollarName <*> pure arg
+replaceExpr e = pure e
+
+[dollarName] = map idName $ filter ((dollarIdKey==) . getUnique) wiredInIds
diff --git a/Language/Haskell/Tools/Refactor/Builtin/DollarApp3.hs b/Language/Haskell/Tools/Refactor/Builtin/DollarApp3.hs
new file mode 100644
--- /dev/null
+++ b/Language/Haskell/Tools/Refactor/Builtin/DollarApp3.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE FlexibleContexts, ConstraintKinds, TypeFamilies #-}
+module Language.Haskell.Tools.Refactor.Builtin.DollarApp3 where
+
+import Language.Haskell.Tools.Refactor
+
+import Id (idName)
+import qualified Name as GHC (Name)
+import PrelInfo (wiredInIds)
+import PrelNames (dollarIdKey)
+import SrcLoc (RealSrcSpan, SrcSpan)
+import Unique (getUnique)
+
+import Control.Monad.State
+import Control.Reference ((!~), biplateRef)
+
+tryItOut :: String -> String -> IO ()
+tryItOut = tryRefactor (localRefactoring . dollarApp)
+
+type DollarMonad dom = StateT [SrcSpan] (LocalRefactor dom)
+type DollarDomain dom = (HasImportInfo dom, HasModuleInfo dom, HasFixityInfo dom, HasNameInfo dom)
+
+dollarApp :: DollarDomain dom => RealSrcSpan -> LocalRefactoring dom
+dollarApp sp = flip evalStateT [] . ((nodesContained sp !~ (\e -> get >>= replaceExpr e))
+                                        >=> (biplateRef !~ parenExpr))
+
+replaceExpr :: DollarDomain dom => Expr dom -> [SrcSpan] -> DollarMonad dom (Expr dom)
+replaceExpr (App fun (Paren arg)) _ = do modify $ (getRange arg :)
+                                         mkInfixApp fun <$> lift (referenceOperator dollarName) <*> pure arg
+replaceExpr e _ = return e
+
+parenExpr :: Expr dom -> DollarMonad dom (Expr dom)
+parenExpr e = (exprLhs !~ parenDollar True) =<< (exprRhs !~ parenDollar False $ e)
+
+parenDollar :: Bool -> Expr dom -> DollarMonad dom (Expr dom)
+parenDollar lhs expr@(InfixApp _ _ arg)
+  = do replacedRanges <- get
+       if getRange arg `elem` replacedRanges && (lhs || getRange expr `notElem` replacedRanges)
+         then return $ mkParen expr
+         else return expr
+parenDollar _ e = return e
+
+dollarName :: GHC.Name
+[dollarName] = map idName $ filter ((dollarIdKey==) . getUnique) wiredInIds
diff --git a/Language/Haskell/Tools/Refactor/Builtin/HelloRefactor.hs b/Language/Haskell/Tools/Refactor/Builtin/HelloRefactor.hs
new file mode 100644
--- /dev/null
+++ b/Language/Haskell/Tools/Refactor/Builtin/HelloRefactor.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
+module Language.Haskell.Tools.Refactor.Builtin.HelloRefactor where
+
+import Language.Haskell.Tools.PrettyPrint (prettyPrint)
+import Language.Haskell.Tools.Refactor
+
+import Control.Reference ((.-))
+import Debug.Trace (trace)
+import SrcLoc (RealSrcSpan)
+
+tryItOut :: String -> String -> IO ()
+tryItOut = tryRefactor (localRefactoring . helloRefactor)
+
+helloRefactor :: Domain dom => RealSrcSpan -> LocalRefactoring dom
+helloRefactor sp = return . (nodesContained sp .- helloExpr)
+
+helloExpr :: Expr dom -> Expr dom
+helloExpr e = trace ("\n### Hello: " ++ prettyPrint e) $ e
diff --git a/Language/Haskell/Tools/Refactor/Builtin/IfToGuards.hs b/Language/Haskell/Tools/Refactor/Builtin/IfToGuards.hs
new file mode 100644
--- /dev/null
+++ b/Language/Haskell/Tools/Refactor/Builtin/IfToGuards.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE RankNTypes, FlexibleContexts, TypeFamilies #-}
+module Language.Haskell.Tools.Refactor.Builtin.IfToGuards (ifToGuards, tryItOut) where
+
+import Control.Reference ((^.), (.-), (&))
+import Data.Generics.Uniplate.Data ()
+import Language.Haskell.Tools.Refactor
+import SrcLoc (RealSrcSpan)
+
+tryItOut :: String -> String -> IO ()
+tryItOut = tryRefactor (localRefactoring . ifToGuards)
+
+ifToGuards :: Domain dom => RealSrcSpan -> LocalRefactoring dom
+ifToGuards sp = return . (nodesContaining sp .- changeBindings)
+
+changeBindings :: ValueBind dom -> ValueBind dom
+changeBindings (SimpleBind (VarPat name) (UnguardedRhs (If pred thenE elseE)) locals)
+  = mkFunctionBind [mkMatch (mkMatchLhs name []) (createSimpleIfRhss pred thenE elseE) (locals ^. annMaybe) ]
+changeBindings fbs@(FunctionBind {})
+  = funBindMatches&annList&matchRhs .- trfRhs $ fbs
+  where trfRhs :: Rhs dom -> Rhs dom
+        trfRhs (UnguardedRhs (If pred thenE elseE)) = createSimpleIfRhss pred thenE elseE
+        trfRhs e = e -- don't transform already guarded right-hand sides to avoid multiple evaluation of the same condition
+changeBindings b = b
+
+createSimpleIfRhss :: Expr dom -> Expr dom -> Expr dom -> Rhs dom
+createSimpleIfRhss pred thenE elseE = mkGuardedRhss [ mkGuardedRhs [mkGuardCheck pred] thenE
+                                                    , mkGuardedRhs [mkGuardCheck (mkVar (mkName "otherwise"))] elseE
+                                                    ]
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/examples/Refactor/DataToNewtype/Cases.hs b/examples/Refactor/DataToNewtype/Cases.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DataToNewtype/Cases.hs
@@ -0,0 +1,8 @@
+module Refactor.DataToNewtype.Cases where
+
+data A = A Int
+
+newtype B = B Int
+data C = C
+data D = D Int Int
+data E
diff --git a/examples/Refactor/DataToNewtype/Cases_res.hs b/examples/Refactor/DataToNewtype/Cases_res.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DataToNewtype/Cases_res.hs
@@ -0,0 +1,8 @@
+module Refactor.DataToNewtype.Cases where
+
+newtype A = A Int
+
+newtype B = B Int
+data C = C
+data D = D Int Int
+data E
diff --git a/examples/Refactor/DollarApp/AnotherOperator.hs b/examples/Refactor/DollarApp/AnotherOperator.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DollarApp/AnotherOperator.hs
@@ -0,0 +1,5 @@
+module Refactor.DollarApp.AnotherOperator where
+
+import Refactor.DollarApp.Defs
+
+x = f (g $$ 3)
diff --git a/examples/Refactor/DollarApp/AnotherOperator_res.hs b/examples/Refactor/DollarApp/AnotherOperator_res.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DollarApp/AnotherOperator_res.hs
@@ -0,0 +1,5 @@
+module Refactor.DollarApp.AnotherOperator where
+
+import Refactor.DollarApp.Defs
+
+x = f (g $$ 3)
diff --git a/examples/Refactor/DollarApp/Defs.hs b/examples/Refactor/DollarApp/Defs.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DollarApp/Defs.hs
@@ -0,0 +1,9 @@
+module Refactor.DollarApp.Defs where
+
+f = id
+g = id
+
+($$) :: (a -> b) -> a -> a
+f $$ a = a
+
+infixl 0 $$
diff --git a/examples/Refactor/DollarApp/Final.hs b/examples/Refactor/DollarApp/Final.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DollarApp/Final.hs
@@ -0,0 +1,16 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+module Refactor.DollarApp.Final where
+
+import Prelude ((+))
+
+import A
+
+x = f (g 1)
+x2 = f (f (g 2))
+
+x3 = f (g 3) + 3
+x4 = 3 + f (g 4)
+
+x5 = f (g $$ 1)
+x6 = f (1 + 1)
+
diff --git a/examples/Refactor/DollarApp/FirstMulti.hs b/examples/Refactor/DollarApp/FirstMulti.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DollarApp/FirstMulti.hs
@@ -0,0 +1,5 @@
+module Refactor.DollarApp.FirstMulti where
+
+import Refactor.DollarApp.Defs
+
+x = f (f (g 2))
diff --git a/examples/Refactor/DollarApp/FirstMulti_res.hs b/examples/Refactor/DollarApp/FirstMulti_res.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DollarApp/FirstMulti_res.hs
@@ -0,0 +1,5 @@
+module Refactor.DollarApp.FirstMulti where
+
+import Refactor.DollarApp.Defs
+
+x = f $ f $ g 2
diff --git a/examples/Refactor/DollarApp/FirstSingle.hs b/examples/Refactor/DollarApp/FirstSingle.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DollarApp/FirstSingle.hs
@@ -0,0 +1,5 @@
+module Refactor.DollarApp.FirstSingle where
+
+import Refactor.DollarApp.Defs
+
+x = f (g 1)
diff --git a/examples/Refactor/DollarApp/FirstSingle_res.hs b/examples/Refactor/DollarApp/FirstSingle_res.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DollarApp/FirstSingle_res.hs
@@ -0,0 +1,5 @@
+module Refactor.DollarApp.FirstSingle where
+
+import Refactor.DollarApp.Defs
+
+x = f $ g 1
diff --git a/examples/Refactor/DollarApp/ImportDollar.hs b/examples/Refactor/DollarApp/ImportDollar.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DollarApp/ImportDollar.hs
@@ -0,0 +1,6 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+module Refactor.DollarApp.ImportDollar where
+
+import Refactor.DollarApp.Defs
+
+x = f (g x)
diff --git a/examples/Refactor/DollarApp/ImportDollar_res.hs b/examples/Refactor/DollarApp/ImportDollar_res.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DollarApp/ImportDollar_res.hs
@@ -0,0 +1,7 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+module Refactor.DollarApp.ImportDollar where
+
+import Refactor.DollarApp.Defs
+import qualified GHC.Base(($))
+
+x = f GHC.Base.$ g x
diff --git a/examples/Refactor/DollarApp/InfixOperator.hs b/examples/Refactor/DollarApp/InfixOperator.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DollarApp/InfixOperator.hs
@@ -0,0 +1,5 @@
+module Refactor.DollarApp.InfixOperator where
+
+import Refactor.DollarApp.Defs
+
+x = f (g 3) + 3
diff --git a/examples/Refactor/DollarApp/InfixOperator_res.hs b/examples/Refactor/DollarApp/InfixOperator_res.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/DollarApp/InfixOperator_res.hs
@@ -0,0 +1,5 @@
+module Refactor.DollarApp.InfixOperator where
+
+import Refactor.DollarApp.Defs
+
+x = (f $ g 3) + 3
diff --git a/examples/Refactor/IfToGuards/Simple.hs b/examples/Refactor/IfToGuards/Simple.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/IfToGuards/Simple.hs
@@ -0,0 +1,3 @@
+module Refactor.IfToGuards.Simple where
+
+max a b = if a > b then a else b
diff --git a/examples/Refactor/IfToGuards/Simple_res.hs b/examples/Refactor/IfToGuards/Simple_res.hs
new file mode 100644
--- /dev/null
+++ b/examples/Refactor/IfToGuards/Simple_res.hs
@@ -0,0 +1,4 @@
+module Refactor.IfToGuards.Simple where
+
+max a b | a > b = a
+        | otherwise = b
diff --git a/haskell-tools-experimental-refactorings.cabal b/haskell-tools-experimental-refactorings.cabal
new file mode 100644
--- /dev/null
+++ b/haskell-tools-experimental-refactorings.cabal
@@ -0,0 +1,75 @@
+name:                haskell-tools-experimental-refactorings
+version:             1.0.0.0
+synopsis:            Refactoring Tool for Haskell
+description:         Contains experimental refactorings implemented in the Haskell-tools framework for tutorial purposes, or to be added later to the set of mature refactorings.
+homepage:            https://github.com/haskell-tools/haskell-tools
+license:             BSD3
+license-file:        LICENSE
+author:              Boldizsar Nemeth
+maintainer:          nboldi@elte.hu
+category:            Language
+build-type:          Simple
+cabal-version:       >=1.10
+
+extra-source-files: examples/Refactor/DataToNewtype/*.hs
+                  , examples/Refactor/DollarApp/*.hs
+                  , examples/Refactor/IfToGuards/*.hs
+
+library
+  exposed-modules:     Language.Haskell.Tools.Refactor.Builtin.DataToNewtype
+                     , Language.Haskell.Tools.Refactor.Builtin.IfToGuards
+                     , Language.Haskell.Tools.Refactor.Builtin.DollarApp
+                     , Language.Haskell.Tools.Refactor.Builtin.HelloRefactor
+                     , Language.Haskell.Tools.Refactor.Builtin.DollarApp1
+                     , Language.Haskell.Tools.Refactor.Builtin.DollarApp2
+                     , Language.Haskell.Tools.Refactor.Builtin.DollarApp3
+
+  build-depends:       base                      >= 4.10  && < 4.11
+                     , mtl                       >= 2.2  && < 2.3
+                     , uniplate                  >= 1.6  && < 1.7
+                     , ghc-paths                 >= 0.1  && < 0.2
+                     , containers                >= 0.5  && < 0.6
+                     , directory                 >= 1.2  && < 1.4
+                     , transformers              >= 0.5  && < 0.6
+                     , references                >= 0.3  && < 0.4
+                     , split                     >= 0.2  && < 0.3
+                     , filepath                  >= 1.4  && < 1.5
+                     , template-haskell          >= 2.12 && < 2.13
+                     , ghc                       >= 8.2  && < 8.3
+                     , Cabal                     >= 2.0 && < 2.1
+                     , haskell-tools-ast         >= 1.0  && < 1.1
+                     , haskell-tools-backend-ghc >= 1.0  && < 1.1
+                     , haskell-tools-rewrite     >= 1.0  && < 1.1
+                     , haskell-tools-prettyprint >= 1.0  && < 1.1
+                     , haskell-tools-refactor    >= 1.0  && < 1.1
+  default-language:    Haskell2010
+
+test-suite haskell-tools-experimental-refactorings-test
+  type:                exitcode-stdio-1.0
+  ghc-options:         -with-rtsopts=-M2g
+  hs-source-dirs:      test
+  main-is:             Main.hs
+  build-depends:       base                      >= 4.10  && < 4.11
+                     , tasty                     >= 0.11 && < 0.12
+                     , tasty-hunit               >= 0.9 && < 0.10
+                     , transformers              >= 0.5  && < 0.6
+                     , either                    >= 4.4  && < 4.5
+                     , filepath                  >= 1.4  && < 1.5
+                     , mtl                       >= 2.2  && < 2.3
+                     , uniplate                  >= 1.6  && < 1.7
+                     , containers                >= 0.5  && < 0.6
+                     , directory                 >= 1.2  && < 1.4
+                     , references                >= 0.3  && < 0.4
+                     , split                     >= 0.2  && < 0.3
+                     , time                      >= 1.6  && < 1.9
+                     , template-haskell          >= 2.12 && < 2.13
+                     , ghc                       >= 8.2  && < 8.3
+                     , ghc-paths                 >= 0.1  && < 0.2
+                     , Cabal                     >= 2.0 && < 2.1
+                     , haskell-tools-ast         >= 1.0  && < 1.1
+                     , haskell-tools-backend-ghc >= 1.0  && < 1.1
+                     , haskell-tools-rewrite     >= 1.0  && < 1.1
+                     , haskell-tools-prettyprint >= 1.0  && < 1.1
+                     , haskell-tools-refactor    >= 1.0  && < 1.1
+                     , haskell-tools-experimental-refactorings
+  default-language:    Haskell2010
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE LambdaCase
+           , TypeFamilies
+           #-}
+module Main where
+
+import Test.Tasty (TestTree, testGroup, defaultMain)
+import Test.Tasty.HUnit (assertEqual, testCase)
+
+import GHC hiding (loadModule, ParsedModule)
+import GHC.Paths ( libdir )
+
+import Control.Monad (Monad(..))
+import Data.Either.Combinators (mapRight)
+import System.FilePath (pathSeparator, (</>))
+import System.IO
+
+import Language.Haskell.Tools.AST as AST (IdDom)
+import Language.Haskell.Tools.PrettyPrint (prettyPrint)
+import Language.Haskell.Tools.Refactor
+import Language.Haskell.Tools.Refactor.Builtin.DataToNewtype (dataToNewtype)
+import Language.Haskell.Tools.Refactor.Builtin.DollarApp (dollarApp)
+import Language.Haskell.Tools.Refactor.Builtin.IfToGuards (ifToGuards)
+
+main :: IO ()
+main = defaultMain $ testGroup "experimental refactor tests" functionalTests
+
+functionalTests :: [TestTree]
+functionalTests = map makeMiscRefactorTest miscRefactorTests
+
+rootDir = "examples"
+
+miscRefactorTests =
+  [ ("Refactor.DataToNewtype.Cases", \_ -> dataToNewtype)
+  , ("Refactor.IfToGuards.Simple", \m -> ifToGuards (correctRefactorSpan m $ readSrcSpan "3:11-3:33"))
+  , ("Refactor.DollarApp.FirstSingle", \m -> dollarApp (correctRefactorSpan m $ readSrcSpan "5:5-5:12"))
+  , ("Refactor.DollarApp.FirstMulti", \m -> dollarApp (correctRefactorSpan m $ readSrcSpan "5:5-5:16"))
+  , ("Refactor.DollarApp.InfixOperator", \m -> dollarApp (correctRefactorSpan m $ readSrcSpan "5:5-5:16"))
+  , ("Refactor.DollarApp.AnotherOperator", \m -> dollarApp (correctRefactorSpan m $ readSrcSpan "5:5-5:15"))
+  , ("Refactor.DollarApp.ImportDollar", \m -> dollarApp (correctRefactorSpan m $ readSrcSpan "6:5-6:12"))
+  ]
+
+makeMiscRefactorTest :: (String, UnnamedModule IdDom -> LocalRefactoring IdDom) -> TestTree
+makeMiscRefactorTest (moduleName, refact)
+  = testCase moduleName $
+      do expected <- loadExpected True rootDir moduleName
+         res <- testRefactor refact moduleName
+         assertEqual "The transformed result is not what is expected" (Right (standardizeLineEndings expected))
+                                                                      (mapRight standardizeLineEndings res)
+
+testRefactor :: (UnnamedModule IdDom -> LocalRefactoring IdDom) -> String -> IO (Either String String)
+testRefactor refact moduleName
+  = runGhc (Just libdir) $ do
+      initGhcFlags
+      useDirs [rootDir]
+      mod <- loadModule rootDir moduleName >>= parseTyped
+      res <- runRefactor (SourceFileKey (rootDir </> moduleSourceFile moduleName) moduleName, mod) [] (localRefactoring $ refact mod)
+      case res of Right r -> return $ Right $ prettyPrint $ snd $ fromContentChanged $ head r
+                  Left err -> return $ Left err
+
+loadExpected :: Bool -> String -> String -> IO String
+loadExpected resSuffix workingDir moduleName =
+  do -- need to use binary or line endings will be translated
+     expectedHandle <- openBinaryFile (workingDir </> map (\case '.' -> pathSeparator; c -> c) moduleName ++ (if resSuffix then "_res" else "") ++ ".hs") ReadMode
+     hGetContents expectedHandle
+
+standardizeLineEndings = filter (/= '\r')
