Guest User

Untitled

a guest
Feb 17th, 2019
97
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.94 KB | None | 0 0
  1. @file:Suppress("KDocUnresolvedReference")
  2.  
  3. package com.examples.standardfunctions
  4.  
  5. import androidx.annotation.Nullable
  6. import java.util.*
  7. import java.util.function.Function
  8.  
  9. // region Person
  10.  
  11. // region Abstract Person
  12. data class UserId(val value: String = UUID.randomUUID().toString())
  13.  
  14. abstract class Person(
  15. val id: UserId,
  16. val name: String,
  17. val age: Int?,
  18. val profession: String?,
  19. val friends: List<UserId> = emptyList()
  20. )
  21.  
  22. val bob = object : Person(
  23. UserId(),
  24. "Bob",
  25. 25,
  26. "Kotlin Programmer"
  27. ) {}
  28.  
  29. // endregion Abstract Person
  30.  
  31. // region Purl
  32. data class Purl(
  33. val knownProgrammingLanguages: List<String> = emptyList()
  34. ) : Person(
  35. id = UserId(),
  36. name = "Purl",
  37. age = 25,
  38. profession = "Software Developer"
  39. ) {
  40. val purlPrinter = {
  41. println("$name $age $profession $friends $knownProgrammingLanguages")
  42. }
  43. }
  44. // endregion Purl
  45.  
  46. // region Lacy
  47. data class Lacy(
  48. val knownDesignPatterns: List<String> = emptyList(),
  49. val knownProgrammingLanguages: List<String> = emptyList()
  50. ) : Person(
  51. id = UserId(),
  52. name = "Lacy",
  53. age = 32,
  54. profession = "Software Architect"
  55. ) {
  56. val lacyPrinter = {
  57. println("$name $age $profession $friends $knownProgrammingLanguages $knownDesignPatterns")
  58. }
  59. }
  60. // endregion Lacy
  61.  
  62. // endregion Person
  63.  
  64. // region Person Functions
  65.  
  66. // Different ways of declaring functions
  67. fun printDetailsFor(person: Person) {
  68. println("${person.name} ${person.age} ${person.profession} ${person.friends}")
  69. }
  70.  
  71. val personDetailsPrinter = { person: Person ->
  72. println("${person.name} ${person.age} ${person.profession} ${person.friends}")
  73. }
  74.  
  75. fun Person.print() {
  76. println("$name $age $profession $friends")
  77. }
  78.  
  79. fun Person.printExpression(): Unit = println("$name $age $profession $friends")
  80.  
  81. // A function that returns a function
  82. fun getPersonPrinter(): (Person) -> Unit {
  83. return personDetailsPrinter
  84. }
  85.  
  86. // A function expression that returns a function
  87. fun getPersonPrinterExpression(): (Person) -> Unit = personDetailsPrinter
  88.  
  89.  
  90. // Different ways of calling functions
  91. fun callFunctions(vararg functions: () -> Unit) {
  92. functions.forEach { it() }
  93. }
  94.  
  95. fun callPrinters() = callFunctions(
  96. { printDetailsFor(bob) },
  97. { bob.print() },
  98. { bob.printExpression() },
  99. { personDetailsPrinter(bob) },
  100. { getPersonPrinter()(bob) },
  101. { getPersonPrinterExpression()(bob) }
  102. )
  103.  
  104. // endregion Person Functions
  105.  
  106. /** TERMINOLOGY LEGEND
  107. *
  108. * Receiver == [this]
  109. *
  110. * block == A Function
  111. *
  112. * [it] == Automatically declared variable based on type
  113. *
  114. * Scope == Everything that can be accessed from in a function,
  115. * [this] is often implied to be the Scope.
  116. *
  117. * Jump/Return/Break Label == Also known as scope targets e.g. return@run
  118. **/
  119. fun main() {
  120. // callPrinters()
  121.  
  122. // doRun()
  123. // doWith()
  124. // doTRun()
  125. doLet()
  126. // doApply()
  127. // putItAllTogether()
  128.  
  129. }
  130.  
  131. // region run()
  132. fun doRun() {
  133. // Used to execute a block of code from the scope of another function
  134.  
  135. // Signature ==
  136. fun <R> runArbitraryCode(block: () -> R): R = block()
  137.  
  138. run {
  139. println("Arbitrary Code Was Run")
  140. }
  141.  
  142. runArbitraryCode {
  143. println("Arbitrary Code Was Run")
  144. }
  145.  
  146. // Functionally ==
  147. fun randomCode() {
  148. println("Arbitrary Code Was Run")
  149. }
  150. randomCode()
  151.  
  152. // Q: When do I use it?
  153. // A: When you need to run something in a function
  154.  
  155. // Q: When do I use run() instead of T.run()?
  156. // A: When you don't need T and you don't need a reference to the function
  157.  
  158. }
  159. // endregion run()
  160.  
  161. // region with()
  162. // Signature ==
  163. fun <T, R> runCodeScopedTo(receiver: T, block: T.() -> R): R = receiver.block()
  164.  
  165. // Different ways to call with()
  166. fun doWith() {
  167.  
  168. with(bob) {
  169. println("$name $age $profession $friends")
  170. }
  171.  
  172. with(bob) {
  173. printDetailsFor(this)
  174. }
  175.  
  176. with(bob) {
  177. printExpression()
  178. }
  179.  
  180. with(bob, { print() })
  181.  
  182. runCodeScopedTo(bob) {
  183. print()
  184. }
  185.  
  186. // Syntactically ~= too but using generics:
  187. fun doStuffWithVariable(variable: Person) {
  188. val name = variable.name
  189. val age = variable.age
  190. val profession = variable.profession
  191. val friends = variable.friends
  192.  
  193. println("$name $age $profession $friends")
  194. }
  195.  
  196. doStuffWithVariable(bob)
  197.  
  198. println("${bob.name} ${bob.age} ${bob.profession} ${bob.friends}")
  199.  
  200. // Q: When do I use it?
  201. // A: When accessing multiple properties / functions of an object
  202.  
  203. // Q: When do I use with() instead of T.run() or T.apply()?
  204. // A: It really depends but almost never due to awkward null handling
  205. // and a more verbose syntax.
  206. }
  207. // endregion with()
  208.  
  209. // region T.run()
  210. // Signature ==
  211. fun <T, R> T.runArbitraryScopedCode(block: T.() -> R): R = block()
  212.  
  213. // Functionally ~=
  214. fun javaStyleRun(argument: Any?, function: Function<Any?, Any?>) {
  215. function.apply(argument)
  216. }
  217.  
  218. fun doTRun() {
  219. // Usage
  220. bob.run { println("$name $age $profession $friends") }
  221.  
  222. // Argument automatically filled in!! Freakin Sweet!
  223. bob.runArbitraryScopedCode(personDetailsPrinter)
  224.  
  225. javaStyleRun(bob, object : Function<Any?, Any?> {
  226. override fun apply(t: Any?): Any? {
  227. t as Person
  228. println("${t.name} ${t.age} ${t.profession} ${t.friends}")
  229. return t
  230. }
  231. })
  232. // Or this
  233. javaStyleRun(bob, Function { person ->
  234. person as Person
  235. println("${person.name} ${person.age} ${person.profession} ${person.friends}")
  236. })
  237.  
  238. }
  239.  
  240. /*
  241. Q: When do I use it?
  242. A: When accessing multiple properties / functions of an object.
  243.  
  244. Q: When do I use T.run() instead of with()?
  245. A: It really depends but almost always due to better null handling
  246. and a less verbose syntax.
  247.  
  248. Q: When do I use T.run() instead of T.let()?
  249. A: I want the function to be scoped to T and
  250. I don't need the scope to be named explicitly.
  251.  
  252. Q: When do I use T.run() instead of T.apply()?
  253. A: I want the function to be scoped to T and
  254. I am not modifying the state of T or
  255. I need to return a different value then T.
  256. */
  257. // endregion T.run()
  258.  
  259. // region T.let()
  260.  
  261. // When do I use it?
  262. // Mostly to reduce ambiguity caused from nested T.run()s.
  263.  
  264. // Signature ==
  265. inline fun <T, R> T.letRunOnIt(block: (T) -> R): R = block(this)
  266.  
  267. fun doLet() {
  268.  
  269. val oldestAgeWithRuns: Int =
  270. bob.age?.run bob@{
  271. buildStockLacy().age?.run lacy@{
  272. buildStockPurl().age?.run purl@{
  273.  
  274. var result = this@bob
  275. // do work
  276. if (result < this@lacy) result = this@lacy
  277. if (result < this@purl) result = this@purl
  278.  
  279. return@bob result
  280.  
  281. // Wow, that's a lot of @s!
  282. // We can do better!
  283.  
  284. }
  285. }
  286. } ?: 0
  287.  
  288. val oldestAgeWithLets: Int =
  289. bob.age?.let theOuterLet@{ bobsAge ->
  290. buildStockLacy().age?.let { laciesAge ->
  291. buildStockPurl().age?.letRunOnIt { purlsAge ->
  292.  
  293. var result = bobsAge
  294. // do work
  295. if (result < laciesAge) result = laciesAge
  296. if (result < purlsAge) result = purlsAge
  297.  
  298. return@theOuterLet result
  299.  
  300. // That's super nice!!
  301.  
  302. }
  303. }
  304. } ?: 0
  305.  
  306. println(oldestAgeWithLets)
  307.  
  308. println(oldestAgeWithRuns)
  309.  
  310. /*
  311. Functionally == too?
  312. When to use it?
  313. */
  314. }
  315.  
  316. /*
  317. Q: When do I use it?
  318. A: The same reason you would use run but you need a named object.
  319. To remove ambiguity in nested T.run()s.
  320.  
  321. Q: When do I use T.let() instead of with()?
  322. A: It really depends but almost always due to better null handling,
  323. less verbose syntax and to reduce ambiguity around [this]
  324.  
  325. Q: When do I use T.let() instead of T.run()?
  326. A: Mostly for the same reasons to use it over with()
  327.  
  328. Q: When do I use T.let() instead of T.apply()?
  329. A: I want the function to be scoped named and
  330. I need to return a different value then T.
  331. */
  332.  
  333. // endregion T.let()
  334.  
  335. // region T.apply()
  336.  
  337. fun doApply() = {
  338.  
  339. // We need to make sure the algorithm works
  340. // so we can use T.apply() to inject some logging between the age!
  341. val oldestAgeWithLets: Int =
  342. bob.age?.let theOuterLet@{ bobsAge ->
  343. buildStockLacy().age?.let { laciesAge ->
  344. buildStockPurl().age?.let { purlsAge ->
  345.  
  346. var result = bobsAge
  347.  
  348. if (result < laciesAge) result = laciesAge
  349. if (result < purlsAge) result = purlsAge
  350.  
  351. return@theOuterLet result
  352. }
  353. }
  354. } ?: 0
  355.  
  356. println(oldestAgeWithLets)
  357.  
  358. }
  359. // endregion T.apply()
  360.  
  361. // region Putting It All Together
  362.  
  363. // Lacy builder DSL made using what we have learned
  364. class LacyBuilderScope {
  365. private val _programmingLanguages = mutableListOf<String>()
  366. private val _designPatterns = mutableListOf<String>()
  367.  
  368. fun addProgrammingLanguage(
  369. block: () -> String
  370. ) = _programmingLanguages.add(block())
  371.  
  372.  
  373. fun addDesignPattern(
  374. block: () -> String
  375. ) = _designPatterns.add(block())
  376.  
  377. fun build() = Lacy(_designPatterns, _programmingLanguages)
  378.  
  379. }
  380.  
  381. fun buildLacy(block: LacyBuilderScope.() -> Unit): Lacy =
  382. LacyBuilderScope().run {
  383. block()
  384. return@run build()
  385. }
  386.  
  387. fun buildStockLacy(block: LacyBuilderScope.() -> Unit = {}): Lacy =
  388. LacyBuilderScope().run {
  389. addProgrammingLanguage { "Kotlin" }
  390. block()
  391. return@run build()
  392. }
  393.  
  394. // Purl builder DSL also made using what we have learned
  395. class PurlBuilderScope {
  396. private val _programmingLanguages = mutableListOf<String>()
  397.  
  398. fun addProgrammingLanguage(
  399. block: () -> String
  400. ) = _programmingLanguages.add(block())
  401.  
  402.  
  403. fun build() = Purl(_programmingLanguages)
  404.  
  405. }
  406.  
  407. fun buildPurl(block: PurlBuilderScope.() -> Unit): Purl =
  408. PurlBuilderScope().run {
  409. block()
  410. return@run build()
  411. }
  412.  
  413. fun buildStockPurl(block: PurlBuilderScope.() -> Unit = {}): Purl =
  414. PurlBuilderScope().run {
  415. addProgrammingLanguage { "Kotlin" }
  416. block()
  417. return@run build()
  418. }
  419.  
  420. fun putItAllTogether() {
  421. val lacy = buildLacy {
  422. addDesignPattern {
  423. "Builder Pattern"
  424. }
  425.  
  426. addProgrammingLanguage {
  427. "Kotlin"
  428. }
  429. }
  430.  
  431. val purl = buildPurl {
  432. addProgrammingLanguage {
  433. "Kotlin"
  434. }
  435. }
  436.  
  437. fun girlsHaveTheSameLanguageSkillSets(
  438. maybePurl: Purl?,
  439. maybeLacy: Lacy?
  440. ): Boolean {
  441. maybePurl?.run {
  442. maybeLacy?.run {
  443. // Both of the class specific functions are available to us!
  444. purlPrinter()
  445. lacyPrinter()
  446. // but how do we differentiate from the two with elegance?
  447. }
  448. }
  449.  
  450. // With a let run or a let let!
  451. var result = false
  452. result = maybePurl?.let { purl: Purl ->
  453. maybeLacy?.run {
  454. return@let knownProgrammingLanguages == purl.knownProgrammingLanguages
  455. }
  456. } ?: false
  457.  
  458. return result
  459. }
  460.  
  461. // Why is this better then Java?
  462. fun girlsHaveTheSameLanguageSkillSetsJavaStyle(
  463. @Nullable maybePurl: Purl?,
  464. @Nullable maybeLacy: Lacy?
  465. ): Boolean {
  466. var result: Boolean = false
  467. if (maybePurl != null && maybeLacy != null) {
  468. maybePurl.purlPrinter()
  469. maybeLacy.lacyPrinter()
  470. result = maybeLacy.knownProgrammingLanguages == maybePurl.knownProgrammingLanguages
  471. }
  472. return result
  473. }
  474.  
  475. // Look at everything that is happening here in just 10 lines of code!
  476. fun girlsHaveTheSameLanguageSkillSetsExpression(
  477. maybePurl: Purl? = buildStockPurl(),
  478. maybeLacy: Lacy? = buildStockLacy()
  479. ): Boolean = maybePurl?.let { purl: Purl ->
  480. maybeLacy?.run {
  481. purl.purlPrinter()
  482. lacyPrinter()
  483. return@let knownProgrammingLanguages == purl.knownProgrammingLanguages
  484. }
  485. } ?: false
  486.  
  487. }
  488.  
  489. // endregion Putting It All Together
Add Comment
Please, Sign In to add comment