Kotlin基础——Typeclass

2024-06-22 18:36
文章标签 基础 kotlin typeclass

本文主要是介绍Kotlin基础——Typeclass,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

高阶类型

如在Iterable新增泛型方法时

interface Iterable<T> {fun filter(p: (T) -> Boolean): Iterable<T>fun remove(p: (T) -> Boolean): Iterable<T> = filter { x -> !p(x) }
}

对应的List、Set实现上述方法时仍需要返回具体的类型

interface List<T> : Iterable<T> {fun filter(p: (T) -> Boolean): List<T>fun remove(p: (T) -> Boolean): List<T> = filter { x -> !p(x) }
}interface Set<T> : Iterable<T> {fun filter(p: (T) -> Boolean): Set<T>fun remove(p: (T) -> Boolean): Set<T> = filter { x -> !p(x) }
}

使用高阶类型可以解决上述问题,高阶类型指的是用类型构造新类型,Kotlin可以通过扩展实现高阶类型(下面例子都是根据这个来实现)

interface Kind<out F, out A>sealed class List<out A> : Kind<List.K, A> {object K
}inline fun <A> Kind<List.K, A>.unwrap(): List<A> = this as List<A>object Nil : List<Nothing>()
data class Cons<A>(val head: A, val tail: List<A>) : List<A>()
  • Kind<out F, out A>表示类型构造器F应用类型参数A产生的新类型,F实际上不能携带类型参数
  • List.K是List的高阶类型,也就是说传入不同的A,根据List.K会有不同类型
  • unwrap()将Kind<List.K, A>类型转为List<A>进行操作
  • Nil为空列表用作尾部,Cons由元素head和及其指向tail构成的链表

Functor

Functor的map():通过f()方法将Kind<F, A>类型转为Kind<F, B>类型

interface Functor<F> {fun <A, B> Kind<F, A>.map(f: (A) -> B): Kind<F, B>
}object ListFunctor : Functor<List.K> {override fun <A, B> Kind<List.K, A>.map(f: (A) -> B): Kind<List.K, B> {return when (this) {is Cons -> {val t = (this.tail.map(f)).unwrap()Cons<B>(f(this.head), t)}else -> Nil}}
}

使用方法如下,将Con<Int>转为了Con<String>

val cons: Cons<Int> = Cons(1, Nil)
println(cons.head::class)
println(cons.tail)
ListFunctor.run {val cons2: Cons<String> = cons.map { it.toString() } as Cons<String>println(cons2.head::class)println(cons2.tail)
}

打印如下

class kotlin.Int
com.demo.demo1.Nil@5361555
class kotlin.String
com.demo.demo1.Nil@5361555

Eq和ListEq

Eq

Eq根据传入的类型参数,对其制定比较规则

interface Eq<F> {fun F.eq(that: F): Boolean
}
object IntEq : Eq<Int> {override fun Int.eq(that: Int): Boolean {return this == that}
}

如上,对于Int,判断值是否相等,使用方法如下

IntEq.run {val a = 1println(a.eq(1))println(a.eq(2))
}

打印如下

true
false

ListEq

ListEq可根据指定类型参数的比较规则,实现对两个List比较

abstract class ListEq<A>(val a: Eq<A>) : Eq<Kind<List.K, A>> {override fun Kind<List.K, A>.eq(that: Kind<List.K, A>): Boolean {val curr = thisreturn if (curr is Cons && that is Cons) {val headEq = a.run {curr.head.eq(that.head)}if (headEq) curr.tail.eq(that.tail) else false} else curr is Nil && that is Nil}
}
object IntListEq : ListEq<Int>(IntEq)

如上,实现IntListEq,使用方法如下

IntListEq.run {val a = Cons(1, Cons(2, Nil))val b = Cons(1, Cons(2, Nil))val c = Cons(1, Nil)println(a.eq(b))println(a.eq(c))
}

打印如下

true
false

show和Foldable

Show

show根据传入的类型参数,对其制定输出规则

interface Show<F> {fun F.show(): String
}
class Book(val name: String)
object BookShow : Show<Book> {override fun Book.show(): String = this.name
}

如上,对于Book,输出name属性,调用方法如下

BookShow.run {println(Book("Dive into Kotlin").show())
}

打印如下

Dive into Kotlin

Foldable

Foldable根据传入的类型参数,对其进行拼接(不太能理解这个fold的实现。。。)

interface Foldable<F> {fun <A, B> Kind<F, A>.fold(init: B): ((B, A) -> B) -> B
}
object ListFoldable : Foldable<List.K> {override fun <A, B> Kind<List.K, A>.fold(init: B): ((B, A) -> B) -> B = { f ->fun fold0(l: List<A>, v: B): B {return when (l) {is Cons -> {fold0(l.tail, f(v, l.head))}else -> v}}fold0(this.unwrap(), init)}
}

ListShow

abstract class ListShow<A>(val a: Show<A>) : Show<Kind<List.K, A>> {override fun Kind<List.K, A>.show(): String {val fa = thisreturn "[" + ListFoldable.run {fa.fold(listOf<String>())({ r, i ->r + a.run { i.show() }}).joinToString() + "]"}}
}
object BookListShow : ListShow<Book>(BookShow)

调用方法如下

BookListShow.run {println(Cons(Book("Dive into Kotlin"),Cons(Book("Thinking in Java"), Nil)).show())
}

打印如下

[Dive into Kotlin, Thinking in Java]

Monoid

Monoid满足结合律和同一律

interface Monoid<A> {fun zero(): Afun A.append(b: A): A
}

如对于字符串Monoid

  • 结合律:(“A”+“B”)+“C” == “A”+(“B”+“C”)
  • 同一律:“A”+“” == “A”
object StringConcatMonoid : Monoid<String> {override fun zero(): String = ""override fun String.append(b: String): String = this + b
}
fun <A> List<A>.sum(ma: Monoid<A>): A {val fa = thisreturn ListFoldable.run {fa.fold(ma.zero())({ s, i ->ma.run {s.append(i)}})}
}

使用方式如下

println(Cons("Dive ",Cons("into ",Cons("Kotlin", Nil))).sum(StringConcatMonoid)
)

打印如下

Dive into Kotlin

Monad

Monad包含了最小的原始操作集合pure()和flatMap(),通过这两个组合,我们可以实现更复杂的数据转换操作

interface Monad<F> {fun <A> pure(a: A): Kind<F, A>fun <A, B> Kind<F, A>.flatMap(f: (A) -> Kind<F, B>): Kind<F, B>
}

如下实现ListMonad

object ListMonad : Monad<List.K> {private fun <A> append(fa: Kind<List.K, A>, fb: Kind<List.K, A>): Kind<List.K, A> {return if (fa is Cons) {Cons(fa.head, append(fa.tail, fb).unwrap())} else {fb}}override fun <A> pure(a: A): Kind<List.K, A> {return Cons(a, Nil)}override fun <A, B> Kind<List.K, A>.flatMap(f: (A) -> Kind<List.K, B>): Kind<List.K, B> {val fa = thisval empty: Kind<List.K, B> = Nilreturn ListFoldable.run {fa.fold(empty)({ r, l ->append(r, f(l))})}}
}

Applicative

数学上3中代数结构关系如下Functor -> Applicative -> Monad

interface Functor<F> {fun <A, B> Kind<F, A>.map(f: (A) -> B): Kind<F, B>
}interface Applicative<F> : Functor<F> {fun <A> pure(a: A): Kind<F, A>fun <A, B> Kind<F, A>.ap(f: Kind<F, (A) -> B>): Kind<F, B>override fun <A, B> Kind<F, A>.map(f: (A) -> B): Kind<F, B> {return ap(pure(f))}
}interface Monad<F> : Applicative<F> {fun <A, B> Kind<F, A>.flatMap(f: (A) -> Kind<F, B>): Kind<F, B>override fun <A, B> Kind<F, A>.ap(f: Kind<F, (A) -> B>): Kind<F, B> {return f.flatMap { fn ->this.flatMap { a ->pure(fn(a))}}}
}

Option和OptionT

Kotlin中没有checked Exception,而是使用类型代替异常处理错误

Either和EitherT

这篇关于Kotlin基础——Typeclass的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1085082

相关文章

MySQL中my.ini文件的基础配置和优化配置方式

《MySQL中my.ini文件的基础配置和优化配置方式》文章讨论了数据库异步同步的优化思路,包括三个主要方面:幂等性、时序和延迟,作者还分享了MySQL配置文件的优化经验,并鼓励读者提供支持... 目录mysql my.ini文件的配置和优化配置优化思路MySQL配置文件优化总结MySQL my.ini文件

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

C 语言基础之数组

文章目录 什么是数组数组变量的声明多维数组 什么是数组 数组,顾名思义,就是一组数。 假如班上有 30 个同学,让你编程统计每个人的分数,求最高分、最低分、平均分等。如果不知道数组,你只能这样写代码: int ZhangSan_score = 95;int LiSi_score = 90;......int LiuDong_score = 100;int Zhou

c++基础版

c++基础版 Windows环境搭建第一个C++程序c++程序运行原理注释常亮字面常亮符号常亮 变量数据类型整型实型常量类型确定char类型字符串布尔类型 控制台输入随机数产生枚举定义数组数组便利 指针基础野指针空指针指针运算动态内存分配 结构体结构体默认值结构体数组结构体指针结构体指针数组函数无返回值函数和void类型地址传递函数传递数组 引用函数引用传参返回指针的正确写法函数返回数组

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口int main(int argc, char *argv[]){// argc是命令行参数个数,argv是

【MRI基础】TR 和 TE 时间概念

重复时间 (TR) 磁共振成像 (MRI) 中的 TR(重复时间,repetition time)是施加于同一切片的连续脉冲序列之间的时间间隔。具体而言,TR 是施加一个 RF(射频)脉冲与施加下一个 RF 脉冲之间的持续时间。TR 以毫秒 (ms) 为单位,主要控制后续脉冲之前的纵向弛豫程度(T1 弛豫),使其成为显著影响 MRI 中的图像对比度和信号特性的重要参数。 回声时间 (TE)

Java基础回顾系列-第七天-高级编程之IO

Java基础回顾系列-第七天-高级编程之IO 文件操作字节流与字符流OutputStream字节输出流FileOutputStream InputStream字节输入流FileInputStream Writer字符输出流FileWriter Reader字符输入流字节流与字符流的区别转换流InputStreamReaderOutputStreamWriter 文件复制 字符编码内存操作流(