diff --git a/cbits/include/hs_mcl_impl.hpp b/cbits/include/hs_mcl_impl.hpp
new file mode 100644
--- /dev/null
+++ b/cbits/include/hs_mcl_impl.hpp
@@ -0,0 +1,259 @@
+#ifndef HAVE_HS_MCL_IMPL
+#define HAVE_HS_MCL_IMPL
+
+#include "HsFFI.h"
+#include "hs_mcl_misc.hpp"
+
+namespace hs_mcl {
+namespace field_impl {
+
+template <typename FpT>
+inline void hash_to(const char *s, const size_t s_length, FpT *result)
+{
+	mem_util::init(result);
+	result->setMsg(s, s_length);
+}
+
+template <typename FpT>
+inline void add(const FpT *a, const FpT *b, FpT *result)
+{
+	mem_util::init(result);
+	FpT::add(*result, *a, *b);
+}
+
+template <typename FpT>
+inline void subtract(const FpT *a, const FpT *b, FpT *result)
+{
+	mem_util::init(result);
+	FpT::sub(*result, *a, *b);
+}
+
+template <typename FpT>
+inline void multiply(const FpT *a, const FpT *b, FpT *result)
+{
+	mem_util::init(result);
+	FpT::mul(*result, *a, *b);
+}
+
+template <typename FpT, typename FrT>
+inline void pow_native(const int const_time, const FpT *a,
+                       const FrT *p, FpT *result)
+{
+	mem_util::init(result);
+	if (const_time)
+		FpT::powCT(*result, *a, *p);
+	else
+		FpT::pow(*result, *a, *p);
+}
+
+template <typename FpT>
+inline void negate(const FpT *a, FpT *result)
+{
+	mem_util::init(result);
+	FpT::neg(*result, *a);
+}
+
+template <typename FpT>
+inline void invert(const FpT *a, FpT *result)
+{
+	mem_util::init(result);
+	FpT::inv(*result, *a);
+}
+
+template <typename FpT>
+inline int eq(const FpT *a, const FpT *b)
+{
+	return *a == *b;
+}
+
+template <typename FpT>
+inline int is_zero(const FpT *a)
+{
+	return a->isZero();
+}
+
+template <typename FpT>
+inline int sqrt(const FpT *a, FpT *result)
+{
+	mem_util::init(result);
+	return FpT::squareRoot(*result, *a);
+}
+
+template <typename FpT>
+inline void to_gmp_integer(const FpT *a, mp_limb_t *result,
+                           const size_t result_length_bytes)
+{
+	mcl::fp::Block bl;
+	a->getBlock(bl);
+
+	static_assert(sizeof(bl.v_[0]) == sizeof(mp_limb_t),
+	              "mcl::fp::Unit and mp_limb_t have different size");
+
+	// Check that we have the exact amount of bytes needed.
+	assert(result_length_bytes == sizeof(bl.v_[0]) * bl.n);
+
+	std::copy(bl.v_, bl.v_ + bl.n, result);
+}
+
+template <typename FpT>
+inline void from_gmp_integer(const mp_limb_t *scalar,
+                             const mp_size_t scalar_limbs,
+                             FpT *result)
+{
+	mem_util::init(result);
+	// Throws if passed value bigger than modulus, we divide modulo in Haskell.
+	result->setArray(scalar, scalar_limbs);
+}
+
+template <typename FpT>
+inline void from_hsint(const HsInt scalar, FpT *result)
+{
+	mem_util::copy(result, scalar);
+}
+
+template <typename FpT, typename... Args>
+inline void from_base(FpT *result, Args&&... args)
+{
+	mem_util::init(result);
+	result->set(std::forward<Args>(args)...);
+}
+
+}
+
+namespace group_impl {
+
+template <typename GrT>
+inline void zero(GrT *result)
+{
+	mem_util::init(result);
+	result->clear();
+}
+
+template <typename FpT, typename GrT>
+inline int construct(const FpT *x, const FpT *y, GrT *result)
+{
+	mem_util::init(result);
+	result->set(*x, *y, false);
+	return result->isValid();
+}
+
+template <typename MapT, typename FpT, typename GrT>
+inline void map_to(MapT map, const FpT *ca, GrT *result)
+{
+	FpT a = *ca;
+	mem_util::init(result);
+	while (true)
+	{
+		try
+		{
+			map(*result, a);
+			break;
+		}
+		catch (cybozu::Exception &)
+		{
+			*a.getFp0() += FpT::BaseFp::one();
+		}
+	}
+}
+
+template <typename GrT>
+inline void add(const GrT *p, const GrT *q, GrT *result)
+{
+	mem_util::init(result);
+	GrT::add(*result, *p, *q);
+}
+
+template <typename GrT>
+inline void invert(const GrT *p, GrT *result)
+{
+	mem_util::init(result);
+	GrT::neg(*result, *p);
+}
+
+template <typename FrT, typename GrT>
+inline void scalar_mul_native(const int const_time,
+                              const FrT *scalar,
+                              const GrT *p,
+                              GrT *result)
+{
+	mem_util::init(result);
+	if (const_time)
+		GrT::mulCT(*result, *p, *scalar);
+	else
+		GrT::mul(*result, *p, *scalar);
+}
+
+template <typename GrT>
+inline void scalar_mul(const int const_time,
+                       const mp_limb_t *scalar,
+                       const mp_size_t scalar_limbs,
+                       const int is_negative,
+                       const GrT *p,
+                       GrT *result)
+{
+	mem_util::init(result);
+	GrT::mulArray(*result, *p, limb_unit_compat(scalar).units,
+	              scalar_limbs, is_negative, const_time);
+}
+
+template <typename FrT, typename GrT>
+inline void scalar_mul_small(const int const_time,
+                             const HsInt scalar,
+                             const GrT *p,
+                             GrT *result)
+{
+	mem_util::init(result);
+	if (const_time)
+		GrT::mulCT(*result, *p, FrT{scalar});
+	else
+		GrT::mul(*result, *p, FrT{scalar});
+}
+
+template <typename GrT>
+inline int eq(const GrT *p, const GrT *q)
+{
+	return *p == *q;
+}
+
+template <typename GrT>
+inline int is_zero(const GrT *p)
+{
+	return p->isZero();
+}
+
+template <typename GrT, typename FpT>
+inline void affine_coords(const GrT *p, FpT *result_x, FpT *result_y)
+{
+	assert(!p->isZero());
+
+	if (p->isNormalized())
+	{
+		mem_util::copy(result_x, p->x);
+		mem_util::copy(result_y, p->y);
+	}
+	else
+	{
+		GrT r{*p};
+		r.normalize();
+		mem_util::copy(result_x, r.x);
+		mem_util::copy(result_y, r.y);
+	}
+}
+
+template <typename GrT, typename FpT>
+inline int y_from_x(const int y_lsb, const FpT *x, FpT *result)
+{
+	int success = 1;
+	mem_util::init(result);
+	try {
+		GrT::getYfromX(*result, *x, y_lsb);
+	} catch (cybozu::Exception &) {
+		success = 0;
+	}
+	return success;
+}
+
+}
+}
+
+#endif // HAVE_HS_MCL_IMPL
diff --git a/cbits/include/hs_mcl_misc.hpp b/cbits/include/hs_mcl_misc.hpp
new file mode 100644
--- /dev/null
+++ b/cbits/include/hs_mcl_misc.hpp
@@ -0,0 +1,102 @@
+#ifndef HAVE_HS_MCL_MISC
+#define HAVE_HS_MCL_MISC
+
+#include <mcl/fp.hpp>
+
+namespace hs_mcl {
+
+// On x86_32 mp_limb_t is a typedef for unsigned long int, whereas mcl::fp::Unit
+// is typedef for unsigned int. These, even though of the same size, are
+// distinct types, hence compiler complains when we pass a pointer to the former
+// when a pointer to the latter is expected. Below is used to safely bridge that
+// gap.
+union limb_unit_compat
+{
+	limb_unit_compat(const mp_limb_t *limbs_)
+		: limbs(limbs_)
+	{ }
+
+	const mp_limb_t *limbs;
+	const mcl::fp::Unit *units;
+
+	static_assert(sizeof(*limbs) == sizeof(*units),
+	              "mp_limb_t and mcl::fp::Unit have different size");
+};
+
+// In order to avoid using foreign pointers to manage memory for MCL numeric
+// classes inside Haskell we take advantage of the fact that all of them are
+// trivially destructible. As there is no need to run the destructor, we can
+// allocate memory for them inside Haskell and construct them at C++ level
+// within that memory block using placement new operator.
+//
+// This class statically guarantees that objects we construct and copy are
+// trivially destructible classes. If at any point in time some of them cease to
+// be so (which is highly doubtful, but nevertheless), it can be easily adjusted
+// to use ordinary operator new (unfortunately then we can't escape ForeignPtr
+// management in Haskell).
+struct mem_util
+{
+	template <typename TargetT>
+	inline static void init(TargetT *target)
+	{
+		static_assert(std::is_class<TargetT>::value,
+		              "Target is not a class");
+
+		mem_util_impl<TargetT, TargetT,
+		              std::is_trivially_destructible<TargetT>::value
+		              >::init(target);
+	}
+
+	template <typename TargetT, typename SourceT>
+	inline static void copy(TargetT *target, SourceT &&source)
+	{
+		static_assert(std::is_class<TargetT>::value,
+		              "Target is not a class");
+
+		mem_util_impl<TargetT, SourceT,
+		              std::is_trivially_destructible<TargetT>::value
+		              >::copy(target,
+		                      std::forward<SourceT>(source));
+	}
+
+private:
+	template <typename TargetT, typename SourceT,
+	          bool is_trivially_destructible>
+	struct mem_util_impl
+	{
+		static_assert(is_trivially_destructible,
+		              "Target is not trivially destructible");
+	};
+
+	template <typename TargetT, typename SourceT>
+	struct mem_util_impl<TargetT, SourceT, true>
+	{
+		inline static void init(TargetT *target)
+		{
+			new (target) TargetT{};
+		}
+
+		inline static void copy(TargetT *target, SourceT &&source)
+		{
+			new (target) TargetT{std::forward<SourceT>(source)};
+		}
+	};
+
+};
+
+////////////////////////////////////////////////////////////
+
+inline void mpz_class_to_gmp_integer(const mpz_class &z, mp_limb_t *result,
+                                     const size_t result_length_bytes)
+{
+	mpz_srcptr mp = z.get_mpz_t();
+	size_t n = mpz_size(mp);
+
+	assert(n * sizeof(mp->_mp_d[0]) == result_length_bytes);
+
+	std::copy(mp->_mp_d, mp->_mp_d + n, result);
+}
+
+}
+
+#endif // HAVE_HS_MCL_MISC
diff --git a/cbits/mcl_fp254bnb.cpp b/cbits/mcl_fp254bnb.cpp
--- a/cbits/mcl_fp254bnb.cpp
+++ b/cbits/mcl_fp254bnb.cpp
@@ -1,5 +1,5 @@
 #include <mcl/bn256.hpp>
-#include "impl.hpp"
+#include "hs_mcl_impl.hpp"
 
 using namespace hs_mcl;
 using namespace mcl::bn256;
diff --git a/mcl.cabal b/mcl.cabal
--- a/mcl.cabal
+++ b/mcl.cabal
@@ -1,7 +1,7 @@
 name:                mcl
-version:             1.0.0
+version:             1.0.1
 synopsis:            Bindings to mcl, a generic and fast pairing-based cryptography library
-description:         Base library: https://github.com/herumi/mcl
+description:         Base library: <https://github.com/herumi/mcl>
 license:             BSD3
 license-file:        LICENSE
 author:              Andrzej Rybczak
@@ -55,6 +55,10 @@
   default-language:    Haskell2010
 
   cc-options:          -std=c++11 -Wall -Wextra
+
+  include-dirs:        cbits/include
+  install-includes:    cbits/include/hs_mcl_impl.hpp
+                       cbits/include/hs_mcl_misc.hpp
 
   c-sources:           cbits/mcl_fp254bnb.cpp
 
