Skip to content

Instantly share code, notes, and snippets.

@landonf
Created October 31, 2014 15:31
Show Gist options
  • Save landonf/f07cfedd92aea25f419c to your computer and use it in GitHub Desktop.
Save landonf/f07cfedd92aea25f419c to your computer and use it in GitHub Desktop.
import java.io.IOException
import java.nio.channels.FileChannel
import java.nio.file.{Path, Paths, StandardOpenOption}
import scodec.bits.BitVector
import shapeless.ops.hlist
import scalaz.{-\/, \/, \/-}
type AnalysisScore = Int
//type DataAnalysis[T] = (DataType, AnalysisScore)
type DataError = String
case class DataCursor[T] ()
object DataLink {
import shapeless._
implicit class DataLinkHListSyntax[I, O <: HList] (self: DataLink[I, O]) {
def composedHList (implicit last : hlist.Last[O]) : DataLink[I, last.Out] = self.map(l => last(l))
}
}
case class DataLink[I, O] (follow: I => DataError \/ O) {
import shapeless._
def ::[LI] (lhs: DataLink[LI, I]): DataLink[LI, I :: O :: HNil] = lhs.flatMap { firstVal =>
this.map(firstVal :: _ :: HNil).follow(firstVal)
}
def ~> [B] (rhs: DataLink[O, B]): DataLink[I, B] = flatMap { firstVal =>
rhs.follow(firstVal)
}
def map[B] (f: O => B): DataLink[I, B] = DataLink[I, B] { input =>
DataLink.this.follow(input).map(f)
}
def flatMap[B] (f: O => DataError \/ B): DataLink[I, B] = DataLink[I, B] { input:I =>
DataLink.this.follow(input).flatMap { data =>
f(data)
}
}
}
val stringToInt = DataLink { str:String => \/-(str.toInt) }
val intToBool = DataLink { int:Int => \/-(true) }
val nested = stringToInt :: intToBool
// Demonstrating type-safe access of the full path
\/-(42) == nested.follow("42").map { value =>
val intVal:Int = value(0)
val boolVal:Boolean = value(1)
if (boolVal)
intVal + 42
else
0
}
// Collapsing of HLists representing the full path to a composition of just FirstInput => FinalOutput
nested.follow("42").map(_.last) == nested.composedHList.follow("42")
// Combining of DataLinks to produce a decoding flow.
val stringToPath = DataLink { str:String => \/-(Paths.get(str)) }
val pathToChannel = DataLink { path:Path =>
try {
\/-(FileChannel.open(path, StandardOpenOption.READ))
} catch {
case ioe:IOException => -\/(s"Could not open file $path: ${ioe.getMessage}")
}
}
val channelToBitVector = DataLink { c:FileChannel => \/-(BitVector.fromMmap(c)) }
val httpLog = stringToPath :: (pathToChannel ~> channelToBitVector ~> DataLink { v =>
babelfish.codecs.sslsplit.http.decode(v).map(_._2)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment