ਜਾਣੋ ਕਿ Scala ਨੂੰ JVM 'ਤੇ ਫੰਕਸ਼ਨਲ ਅਤੇ ਆਬਜੈਕਟ-ਓਰੀਐਂਟਡ ਵਿਚਾਰਾਂ ਨੂੰ ਇਕੱਠਾ ਕਰਨ ਲਈ ਕਿਵੇਂ ਡਿਜ਼ਾਇਨ ਕੀਤਾ ਗਿਆ, ਇਸਨੇ ਕੀ ਠੀਕ ਕੀਤਾ, ਅਤੇ ਟੀਮਾਂ ਨੂੰ ਕਿਹੜੇ ਟਰੇਡ-ਆਫ਼ਜ਼ ਦਾ ਧਿਆਨ ਰੱਖਣਾ ਚਾਹੀਦਾ ਹੈ।

Java ਨੇ JVM ਨੂੰ ਕਾਮਯਾਬ ਬਣਾਇਆ, ਪਰ ਉਹਨਾਂ ਨੇ ਕੁਝ ਐਸੀਆਂ ਉਮੀਦਾਂ ਵੀ ਰੱਖੀਆਂ ਜੋ ਬਹੁਤ ਸਾਰੀਆਂ ਟੀਮਾਂ ਨੂੰ ਬਾਅਦ ਵਿੱਚ ਮੁਸ਼ਕਲਾਂ ਵਿੱਚ ਪਾ ਦਿੰਦੀਆਂ: ਬਹੁਤ ਸਾਰਾ ਬੋਇਲਰਪਲੇਟ, ਮਿਊਟੇਬਲ ਸਟੇਟ 'ਤੇ ਜ਼ੋਰ, ਅਤੇ ਅਜਿਹੀਆਂ ਪੈਟਰਨਾਂ ਜੋ ਸੰਭਾਲਣ ਲਈ ਅਕਸਰ ਫਰੈਮਵਰਕ ਜਾਂ ਕੋਡ ਜਨਰੇਸ਼ਨ ਦੀ ਲੋੜ ਪੈਂਦੀ ਸੀ। ਡਿਵੈਲਪਰ JVM ਦੀ ਰਫ਼ਤਾਰ, ਟੂਲਿੰਗ ਅਤੇ ਡਿਪਲੋਇਮੈਂਟ ਕਹਾਣੀ ਨੂੰ ਪਸੰਦ ਕਰਦੇ ਸਨ—ਪਰ ਉਹ ਇੱਕ ਐਸੇ ਭਾਸ਼ਾ ਚਾਹੁੰਦੇ ਸਨ ਜੋ ਵਿਚਾਰਾਂ ਨੂੰ ਸਿੱਧਾ ਦਰਸਾਉਣ ਦੇ ਯੋਗ ਹੋਵੇ।
2000 ਦੇ ਦਹਾਕੇ ਦੇ ਸ਼ੁਰੂ ਤੱਕ, ਰੋਜ਼ਾਨਾ JVM ਕੰਮ ਵਿੱਚ verbose ਕਲਾਸ ਹਾਇਰਾਰਕੀਜ਼, getter/setter ਰਸਮ ਅਤੇ null-ਸੰਬੰਧੀ ਬੱਗ ਆਮ ਸਨ। ਸਮਕਾਲੀ ਪ੍ਰੋਗ੍ਰਾਮ ਲਿਖਣਾ ਸੰਭਵ ਸੀ, ਪਰ ਸਾਂਝੇ ਮਿਊਟੇਬਲ ਸਟੇਟ ਨੇ ਸੁਖੜ ਰੇਸ-ਕੰਡਿਸ਼ਨ ਬਣਾਣਾ ਆਸਾਨ ਕਰ ਦਿੱਤਾ। ਚੰਗੀ OO ਡਿਜ਼ਾਈਨ ਦੇ ਬਾਵਜੂਦ ਭੀ ਦਿਨ-ਪ੍ਰਤੀ-ਦਿਨ ਕੋਡ ਵਿੱਚ ਬਹੁਤ ਸਾਰੀ ਅਣਚਾਹੀ ਜਟਿਲਤਾ ਰਹਿ ਜਾਂਦੀ ਸੀ।
Scala ਦਾ ਦਾਅਵਾ ਇਹ ਸੀ ਕਿ ਇੱਕ ਬਿਹਤਰ ਭਾਸ਼ਾ ਇਸ friction ਨੂੰ ਘਟਾ ਸਕਦੀ ਹੈ ਬਿਨਾਂ JVM ਨੂੰ ਛੱਡੇ: ਬਾਈਟਕੋਡ ਵਿੱਚ ਕੰਪਾਇਲ ਹੋ ਕੇ ਪ੍ਰਦਰਸ਼ਨ ਨੂੰ “ਸ਼ਾਯਦ ਕਾਫੀ” ਰੱਖੋ, ਪਰ ਉਹ ਫੀਚਰ ਦਿਓ ਜੋ ਡਿਵੈਲਪਰਾਂ ਨੂੰ ਡੋਮੇਨ ਸਾਫ਼ ਤਰੀਕੇ ਨਾਲ ਮਾਡਲ ਕਰਨ ਅਤੇ ਬਦਲਾਅ ਆਸਾਨ ਸਿਸਟਮ ਬਣਾਉਣ ਵਿੱਚ ਮਦਦ ਕਰਨ।
ਬਹੁਤ ਸਾਰੀਆਂ JVM ਟੀਮਾਂ “ਪਿਆਰੀਰੀਕ ਫੰਕਸ਼ਨਲ” ਅਤੇ “ਸਿਰਫ਼ ਆਬਜੈਕਟ-ਓਰੀਐਂਟਡ” ਵਿਚਕਾਰ ਚੁਣ ਨਹੀਂ ਰਹੀਆਂ—ਉਹ ਡੈੱਡਲਾਈਨਾਂ ਹੇਠਾਂ ਸੌਫਟਵੇਅਰ ਡੈਲਿਵਰ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰ ਰਹੀਆਂ ਸਨ। Scala ਦਾ ਮੁੱਖ ਉਦੇਸ਼ ਇਹ ਸੀ ਕਿ ਤੁਸੀਂ ਜਿੱਥੇ OO ਫਿੱਟ ਹੋਏ ਉੱਥੇ OO ਵਰਤ ਸਕੋ (ਇਨਕੈਪਸੁਲੇਸ਼ਨ, ਮੋਡੀਊਲ APIs, ਸਰਵਿਸ ਬਾਰਡਰ), ਅਤੇ ਅੰਦਰਲੀ ਲੋਜਿਕ ਵਿੱਚ FP ਵਿਚਾਰ (ਅਪਰਿਵਰਤੀ ਡਾਟਾ, ਐਕਸਪ੍ਰੈਸ਼ਨ-ਕੇਂਦਰਿਤ ਕੋਡ, ਕਾਂਪੋਜ਼ੇਬਲ ਟ੍ਰਾਂਸਫ਼ਾਰਮੇਸ਼ਨ) ਦਿਓ ਤਾਂ ਕਿ ਪ੍ਰੋਗਰਾਮ ਸੇਫ਼ਰ ਅਤੇ ਸੋਚਣ ਵਿੱਚ ਆਸਾਨ ਬਣਣ।
ਇਹ ਮਿਲਾਪ ਉਹੀ ਚੀਜ਼ ਹੈ ਜੋ ਅਸਲ ਸਿਸਟਮਾਂ ਵਿੱਚ ਆਮ ਤੌਰ 'ਤੇ ਬਣਾਈ ਜਾਂਦੀ ਹੈ: ਮੋਡੀਊਲ ਅਤੇ ਸਰਵਿਸਾਂ ਦੇ ਆਲੇ-ਦੁਆਲੇ OO ਸਰਹੱਦ, ਅਤੇ ਉਹਨਾਂ ਮੋਡੀਊਲਾਂ ਦੇ ਅੰਦਰ ਫੰਕਸ਼ਨਲ ਤਕਨੀਕਾਂ ਜੋ ਬੱਗ ਘਟਾਉਂਦੀਆਂ ਅਤੇ ਟੈਸਟਿੰਗ ਸਧਾਰਨ ਕਰਦੀਆਂ ਹਨ।
Scala ਦਾ ਮਕਸਦ ਸਖ਼ਤ ਸਟੇਟਿਕ ਟਾਈਪਿੰਗ, ਬਿਹਤਰ ਕਾਂਪੋਜ਼ਿਸ਼ਨ ਅਤੇ ਰੀਯੂਜ਼, ਅਤੇ ਭਾਸ਼ਾ-ਸਤਹ ਦੇ ਟੂਲ ਦੇਣਾ ਸੀ ਜੋ ਬੋਇਲਰਪਲੇਟ ਘਟਾਉਂਦੇ ਹੋਣ—ਉਹ ਵੀ JVM ਲਾਇਬ੍ਰੇਰੀਆਂ ਅਤੇ ਓਪਰੇਸ਼ਨਾਂ ਨਾਲ ਅਨੁਕੂਲ ਰਹਿੰਦਿਆਂ।
Martin Odersky ਨੇ Scala ਡਿਜ਼ਾਇਨ ਕੀਤਾ ਜਦੋਂ ਉਹ Java ਦੇ generics 'ਤੇ ਕੰਮ ਕਰ ਰਹੇ ਸਨ ਅਤੇ ML, Haskell, Smalltalk ਵਰਗੀਆਂ ਭਾਸ਼ਾਵਾਂ ਵਿੱਚ ਮਜ਼ਬੂਤੀਆਂ ਵੇਖੀਆਂ। Scala ਦੇ ਆਲੇ-ਦੁਆਲੇ ਜੋ ਕਮਿਊਨਿਟੀ ਬਣੀ—ਐਕੈਡੇਮੀਆ, ਐਨਟਰਪ੍ਰਾਈਜ਼ JVM ਟੀਮਾਂ, ਅਤੇ ਬਾਅਦ ਵਿੱਚ ਡਾਟਾ ਇੰਜੀਨੀਅਰਿੰਗ—ਉਸਨੇ ਇਸਨੂੰ ਇੱਕ ਐਸੀ ਭਾਸ਼ਾ ਬਣਾਉਣ ਵਿੱਚ ਮਦਦ ਕੀਤੀ ਜੋ ਸਿਧਾਂਤ ਅਤੇ ਪ੍ਰੋਡਕਸ਼ਨ ਦੀਆਂ ਲੋੜਾਂ ਵਿਚਕਾਰ ਸੰਤੁਲਨ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰਦੀ ਹੈ।
Scala ਇਸ ਕਹਾਵਤ "ਹਰ ਚੀਜ਼ ਇੱਕ ਓਬਜੈਕਟ ਹੈ" ਨੂੰ ਗੰਭੀਰਤਾ ਨਾਲ ਲੈਂਦੀ ਹੈ। ਉਹ ਮੁੱਲ ਜਿਨ੍ਹਾਂ ਨੂੰ ਤੁਸੀਂ ਹੋਰ JVM ਭਾਸ਼ਾਵਾਂ ਵਿੱਚ “primitive” ਸੋਚਦੇ ਹੋ—ਜਿਵੇਂ 1, true, ਜਾਂ 'a'—ਉਹ ਆਮ ਓਬਜੈਕਟ ਵਰਗੇ ਵਿਵਹਾਰ ਕਰਦੇ ਹਨ। ਇਸਦਾ ਮਤਲਬ ਹੈ ਕਿ ਤੁਸੀਂ 1.toString ਜਾਂ 'a'.isLetter ਵਰਗੇ ਕੋਡ ਬਿਨਾਂ ਮਨ ਮੋੜੇ ਲਿਖ ਸਕਦੇ ਹੋ।
ਜੇ ਤੁਸੀਂ Java-ਸ਼ੈਲੀ ਮਾਡਲਿੰਗ ਦੇ ਆਦੀ ਹੋ, ਤਾਂ Scala ਦੀ OO ਸਰਫੇਸ ਲਗਭਗ ਤੁਰੰਤ ਪਛਾਣਯੋਗ ਹੈ: ਤੁਸੀਂ classes ਡਿਫਾਈਨ ਕਰਦੇ ਹੋ, instances ਬਣਾਉਂਦੇ ਹੋ, methods ਕਾਲ ਕਰਦੇ ਹੋ, ਅਤੇ ਵਰਤੋਂ-ਅਨੁਕੂਲ ਤਰੀਕੇ ਨਾਲ ਵਰਤਾਰਾ ਟਾਈਪਾਂ ਨਾਲ ਵਰਤਦੇ ਹੋ।
ਤੁਸੀਂ ਡੋਮੇਨ ਨੂੰ ਸਿੱਧਾ ਤਰੀਕੇ ਨਾਲ ਮਾਡਲ ਕਰ ਸਕਦੇ ਹੋ:
class User(val name: String) {
def greet(): String = s"Hi, $name"
}
val u = new User("Sam")
println(u.greet())
ਉਹੀ ਪਰਿਚਿਤਤਾ JVM 'ਤੇ ਮੱਤ ਰੱਖਦੀ ਹੈ: ਟੀਮਾਂ Scala ਨੂੰ ਅਪਣਾਉਂਦਿਆਂ ਬੁਨਿਆਦੀ “objects with methods” ਸੋਚ ਛੱਡਣ ਦੀ ਲੋੜ ਮਹਿਸੂਸ ਨਹੀਂ ਕਰਦੀਆਂ।
Scala ਦਾ ਓਬਜੈਕਟ ਮਾਡਲ Java ਨਾਲੋਂ ਜ਼ਿਆਦਾ ਇੱਕਸਾਰ ਅਤੇ ਲਚਕੀਲਾ ਹੈ:
object Config { ... }), ਜੋ ਅਕਸਰ Java ਦੇ static ਪੈਟਰਨ ਦੀ ਜਗ੍ਹਾ ਲੈ ਲੈਂਦੇ ਹਨ।val/var ਨਾਲ ਖੇਤਰ ਬਣ ਸਕਦੇ ਹਨ, ਜਿਸ ਨਾਲ ਬੋਇਲਰਪਲੇਟ ਘਟਦਾ ਹੈ।ਵਿਰਾਸਤ ਹਾਲੇ ਵੀ ਮੌਜੂਦ ਹੈ ਅਤੇ ਅਕਸਰ ਵਰਤੀ ਜਾਂਦੀ ਹੈ, ਪਰ ਇਹ ਅਕਸਰ ਹਲਕੀ-ਵਜ਼ਨ ਵਾਲੀ ਹੁੰਦੀ ਹੈ:
class Admin(name: String) extends User(name) {
override def greet(): String = s"Welcome, $name"
}
ਦਿਨ-ਪ੍ਰਤੀ-ਦਿਨ ਦੇ ਕੰਮ ਵਿੱਚ, ਇਹ ਮਤਲਬ ਹੁੰਦਾ ਹੈ ਕਿ Scala ਉਹੀ OO ਬਿਲਡਿੰਗ ਬਲਾਕਸ ਸਹਿਯੋਗ ਕਰਦੀ ਹੈ ਜਿਨ੍ਹਾਂ 'ਤੇ ਲੋਕ ਨਿਰਭਰ ਕਰਦੇ ਹਨ—classes, ਇਨਕੈਪਸੁਲੇਸ਼ਨ, overriding—ਪਰ JVM-ਯੁਗ ਦੀਆਂ ਕੁਝ ਅਣਜਾਣੀਆਂ ਗੱਲਾਂ (ਜਿਵੇਂ ਭਾਰੀ static ਵਰਤੋਂ ਅਤੇ verbose getters/setters) ਨੂੰ ਨਰਮ ਕਰ ਦਿੰਦੀ ਹੈ।
Scala ਦਾ ਫੰਕਸ਼ਨਲ ਪਹਲੂ ਕਿਸੇ ਵੱਖਰੇ “ਮੋਡ” ਵਜੋਂ ਨਹੀਂ ਹੁੰਦਾ—ਇਹ ਭਾਸ਼ਾ ਵਿਚ ਉਹ ਡਿਫੌਲਟ ਨਿਯਮਾਂ ਵਿੱਚ ਦਿਖਾਈ ਦਿੰਦਾ ਹੈ ਜਿਨ੍ਹਾਂ ਵੱਲ ਭਾਸ਼ਾ ਤੁਹਾਨੂੰ ਧਕੇਲਦੀ ਹੈ। ਦੋ ਵਿਚਾਰ ਇਸਦਾ ਮੁਖ ਚਲਕ ਹਨ: ਅਪਰਿਵਰਤੀ ਡੇਟਾ ਨੂੰ ਤਰਜੀਹ ਦੋ, ਅਤੇ ਆਪਣੇ ਕੋਡ ਨੂੰ ਐਕਸਪ੍ਰੈਸ਼ਨਾਂ ਵਜੋਂ ਲਿਖੋ ਜੋ ਮੁੱਲਤ ਉਤਪਾਦਦੇ ਹਨ।
Scala ਵਿੱਚ, ਤੁਸੀਂ val ਨਾਲ ਮੁੱਲ ਘੋਸ਼ਿਤ ਕਰਦੇ ਹੋ ਅਤੇ var ਨਾਲ ਵੈਰੀਏਬਲ। ਦੋਹਾਂ ਮੌਜੂਦ ਹਨ, ਪਰ ਸੱਭਿਆਚਾਰਕ ਡਿਫਾਲਟ val ਹੈ।
ਜਦੋਂ ਤੁਸੀਂ val ਵਰਤਦੇ ਹੋ, ਤੁਸੀਂ ਕਹਿ ਰਹੇ ਹੋ: “ਇਹ ਰੇਫਰੈਂਸ ਮੁੜ-ਸੋਨਪ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ।” ਇਹ ਛੋਟਾ-ਜਿਹਾ ਚੋਣ ਤੁਹਾਡੇ ਪ੍ਰੋਗਰਾਮ ਵਿੱਚ ਲੁਕੇ ਹੋਏ ਸਟੇਟ ਦੀ ਮਾਤਰਾ ਘਟਾਉਂਦੀ ਹੈ। ਘੱਟ ਸਟੇਟ ਦਾ ਮਤਲਬ ਹੈ ਘੱਟ ਹੈਰਾਨੀਆਂ, ਖ਼ਾਸ ਕਰਕੇ ਜਦੋਂ ਕੋਡ ਵੱਧਦਾ ਹੈ।
var ਦਾ ਅਜੇ ਵੀ ਆਪਣਾ ਸਥਾਨ ਹੈ—UI ਗਲੂ ਕੋਡ, ਕਾਊਂਟਰ, ਜਾਂ ਪਰਫਾਰਮੈਂਸ-ਸੰਵੇਦਨਸ਼ੀਲ ਹਿੱਸੇ—ਪਰ ਇਸਨੂੰ ਵਰਤਣਾ ਜਾਣ-ਬੂਝ ਕੇ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ ਨਾ ਕਿ ਆਟੋਮੈਟਿਕ ਤੌਰ 'ਤੇ।
Scala ਤੁਹਾਨੂੰ ਐਕਸਪ੍ਰੈਸ਼ਨ ਵਜੋਂ ਲਿਖਣ ਲਈ ਪ੍ਰੇਰਿਤ ਕਰਦੀ ਹੈ ਜੋ ਇੱਕ ਨਤੀਜਾ ਉਤਪਾਦਦੇ ਹਨ, ਬਜਾਏ ਉਸ ਦੇ ਕਿ ਕਈ ਸਟੇਟ ਅਪਡੇਟ ਕਰਨ ਵਾਲੀਆਂ ਕਮਾਂਡਾਂ ਦੀ ਲੜੀ ਹੋਵੈ।
ਇਹ ਅਕਸਰ ਛੋਟੇ-ਠੋਕੇ ਨਤੀਜਿਆਂ ਤੋਂ ਇੱਕ ਨਤੀਜਾ ਬਣਾਉਣ ਵਰਗਾ ਦਿਖਦਾ ਹੈ:
val discounted =
if (isVip) price * 0.9
else price
ਇੱਥੇ, if ਇੱਕ ਐਕਸਪ੍ਰੈਸ਼ਨ ਹੈ, ਇਸ ਲਈ ਇਹ ਇੱਕ ਮੁੱਲ ਵਾਪਸ ਕਰਦਾ ਹੈ। ਇਹ ਸ਼ੈਲੀ ਪੁੱਛਣ ਨੂੰ ਆਸਾਨ ਬਣਾਉਂਦੀ ਹੈ: “ਇਹ ਮੁੱਲ ਕੀ ਹੈ?” ਬਜਾਏ ਇੱਕ ਲੰਮੇ ਅਸਾਈਨਮੈਂਟ ਟ੍ਰੇਲ ਨੂੰ ਟਰੇਸ ਕਰਨ ਦੀ।
ਲੂਪਾਂ ਦੀ ਥਾਂ, ਜੋ ਕਲੈਕਸ਼ਨਾਂ ਨੂੰ ਮੋਡੀਫਾਈ ਕਰਦੇ ਹਨ, Scala ਕੋਡ ਆਮ ਤੌਰ 'ਤੇ ਡੇਟਾ ਨੂੰ ਤਬਦੀਲ ਕਰਦਾ ਹੈ:
val emails = users
.filter(_.isActive)
.map(_.email)
filter ਅਤੇ map higher-order ਫੰਕਸ਼ਨ ਹਨ: ਉਹ ਹੋਰ ਫੰਕਸ਼ਨਾਂ ਨੂੰ ਇਨਪੁਟ ਵਜੋਂ ਲੈਂਦੇ ਹਨ। ਫਾਇਦਾ ਅਕਾਦਮਿਕ ਨਹੀਂ—ਇਹ ਸਪਸ਼ਟਤਾ ਹੈ। ਤੁਸੀਂ pipeline ਨੂੰ ਇੱਕ ਛੋਟੀ ਕਹਾਣੀ ਵਜੋਂ ਪੜ੍ਹ ਸਕਦੇ ਹੋ: active users ਰੱਖੋ, ਫਿਰ ਉਹਨਾਂ ਦੇ email ਲਵੋ।
ਇੱਕ ਪਿਊਰ ਫੰਕਸ਼ਨ ਸਿਰਫ਼ ਆਪਣੇ ਇਨਪੁੱਟ 'ਤੇ ਨਿਰਭਰ ਕਰਦਾ ਹੈ ਅਤੇ ਕਿਸੇ ਸਾਈਡ-ਇਫੈਕਟ (ਲਿਖਤ, I/O) ਨਹੀਂ ਕਰਦਾ। ਜਦੋਂ ਤੁਹਾਡਾ ਕੋਡ ਜ਼ਿਆਦਾ ਪਿਊਰ ਹੁੰਦਾ ਹੈ, ਟੈਸਟਿੰਗ ਸਿੱਧੀ ਹੋ ਜਾਂਦੀ ਹੈ: ਤੁਸੀਂ ਇਨਪੁੱਟ ਪਾਸ ਕਰੋ, ਨਤੀਜਿਆਂ ਦੀ ਪੜਚੋਲ ਕਰੋ। ਸੋਚ-ਵਿਚਾਰ ਵੀ ਆਸਾਨ ਹੋ ਜਾਂਦਾ ਹੈ ਕਿਉਂਕਿ ਤੁਹਾਨੂੰ ਇਹ ਅੰਦਾਜ਼ਾ ਨਹੀਂ ਲਗਾਉਣਾ ਪੈਂਦਾ ਕਿ ਸਿਸਟਮ ਦੇ ਕਿਸੇ ਹੋਰ ਹਿੱਸੇ ਨੇ ਕੀ ਬਦਲਿਆ।
Scala ਦਾ ਸਵਾਲ “ਅਸੀਂ ਵਿਹਾਰ ਸਾਂਝਾ ਕਰਨ ਲਈ ਇਕ ਵੱਡਾ ਕਲਾਸ ਟ੍ਰੀ ਬਣਾਉਣ ਦੀ ਥਾਂ ਕੀ ਕਰੀਏ?” ਦਾ ਜਵਾਬ trait ਹੈ। Trait ਇੰਟਰਫੇਸ ਵਰਗਾ ਲੱਗਦਾ ਹੈ, ਪਰ ਇਸ ਵਿੱਚ ਅਸਲ ਇੰਪਲੀਮੇਂਟੇਸ਼ਨ—ਮੈਥਡ, ਫੀਲਡ, ਛੋਟੇ ਹੈਲਪਰ ਲੋਜਿਕ—ਵੀ ਹੋ ਸਕਦੀ ਹੈ।
Traits ਤੁਹਾਨੂੰ ਇੱਕ ਯੋਗਤਾ ਵਰਣਨ ਕਰਨ ਦਿੰਦੀਆਂ ਹਨ ("ਲੌਗ ਕਰ ਸਕਦਾ", "ਵੈਲੀਡੇਟ ਕਰ ਸਕਦਾ", "cache ਕਰ ਸਕਦਾ") ਅਤੇ ਫਿਰ ਉਸ ਯੋਗਤਾ ਨੂੰ ਕਈ ਵੱਖ-ਵੱਖ ਕਲਾਸਾਂ ਨਾਲ ਜੁੜਨ ਦਿੰਦੀਆਂ ਹਨ। ਇਸ ਨਾਲ ਛੋਟੇ, ਕੇਂਦਰਿਤ ਬਿਲਡਿੰਗ ਬਲਾਕਸ ਬਣਾਉਣ ਦੀ ਹਿਮਾਇਤ ਹੁੰਦੀ ਹੈ ਬਜਾਏ ਕਿ ਦਮਦਾਰ ਬੇਸ ਕਲਾਸਾਂ ਬਣਾਈਆਂ ਜਾਣ ਜਿਨ੍ਹਾਂ ਨੂੰ ਹਰ ਕੋਈ ਵਿਰਾਸਤ ਕਰੇ।
Traits single-inheritance ਕਲਾਸ ਹਾਇਰਾਰਕੀਜ਼ ਦੀ ਥਾਂ multiple inheritance of behavior ਲਈ ਬਣਾਏ ਗਏ ਹਨ ਇੱਕ ਨਿਯੰਤਰਿਤ ਤਰੀਕੇ ਨਾਲ। ਤੁਸੀਂ ਇੱਕ ਕਲਾਸ ਵਿੱਚ ਇੱਕ ਤੋਂ ਜ਼ਿਆਦਾ traits ਜੋੜ ਸਕਦੇ ਹੋ, ਅਤੇ Scala ਇੱਕ ਸਪੱਸ਼ਟ linearization ਆਰਡਰ ਪਰਿਭਾਸ਼ਿਤ ਕਰਦਾ ਹੈ ਕਿ ਮੈਥਡ ਕਿਵੇਂ ਹੱਲ ਕੀਤੇ ਜਾਂਦੇ ਹਨ।
ਜਦੋਂ ਤੁਸੀਂ traits “mix in” ਕਰਦੇ ਹੋ, ਤੁਸੀਂ ਵਰਤਾਰਾ ਕਲਾਸ ਸੀਮਾ ਤੇ ਰਚਦੇ ਹੋ ਨਾ ਕਿ ਵਿਰਾਸਤ ਵਿੱਚ ਹੋਰ ਉਤਰਦੇ ਹੋ। ਇਹ ਆਮ ਤੌਰ 'ਤੇ ਰੱਖ-ਰੱਖਾਅ ਲਈ ਆਸਾਨ ਹੁੰਦਾ ਹੈ:
ਸਧਾਰਣ ਉਦਾਹਰਣ:
trait Timestamped { def now(): Long = System.currentTimeMillis() }
trait ConsoleLogging { def log(msg: String): Unit = println(msg) }
class Service extends Timestamped with ConsoleLogging {
def handle(): Unit = log(s"Handled at ${now()}")
}
Traits ਵਰਤੋਂ ਜਦੋਂ:
Abstract class ਵਰਤੋਂ ਜਦੋਂ:
ਅਸਲ ਫ਼ਾਇਦਾ ਇਹ ਹੈ ਕਿ Scala ਨਾਲ ਮੁੜ-ਵਰਤੋਂ ਹਿੱਸਿਆਂ ਦੀ ਤਰ੍ਹਾਂ ਅਸੈਂਬਲ ਕਰਨ ਵਰਗੀ ਮਹਿਸੂਸ ਹੁੰਦੀ ਹੈ ਨਾ ਕਿ ਕਿਸਮਤ ਵਿਰਾਸਤ ਕਰਨ ਤਰ੍ਹਾਂ।
Scala ਦੀ pattern matching ਉਸ ਫੀਚਰਾਂ ਵਿੱਚੋਂ ਇੱਕ ਹੈ ਜੋ ਭਾਸ਼ਾ ਨੂੰ ਮਜ਼ਬੂਤ ਤੌਰ 'ਤੇ "ਫੰਕਸ਼ਨਲ" ਮਹਿਸੂਸ ਕਰਵਾਉਂਦਾ ਹੈ, ਹਾਲਾਂਕਿ ਇਹ ਹਾਲੇ ਵੀ ਕਲਾਸਿਕ OO ਡਿਜ਼ਾਈਨ ਨੂੰ ਸਹਾਰਦਾ ਹੈ। ਵਰਤਣ ਦੀ ਥਾਂ ਇੱਥੇ ਹੈ ਕਿ ਤੁਸੀਂ ਕਿਸੇ ਮੁੱਲ ਦੀ ਆਕਾਰ-ਢਾਂਚਾ ਦੇ ਅਧਾਰ 'ਤੇ ਉਸਦੀ ਜਾਂਚ ਕਰਕੇ ਵਿਵਹਾਰ ਚੁਣ ਸਕਦੇ ਹੋ।
ਸਭ ਤੋਂ ਸਧਾਰਨ ਰੂਪ ਵਿੱਚ, pattern matching ਇੱਕ ਥੋੜ੍ਹਾ ਜਿਆਦਾ ਸ਼ਕਤੀਸ਼ਾਲੀ switch ਹੈ: ਇਹ constant, ਟਾਈਪ, nested ਢਾਂਚਿਆਂ 'ਤੇ ਮੇਲ ਖਾਂਦਾ ਹੈ ਅਤੇ ਗਿਣੇ ਹੋਏ ਹਿੱਸਿਆਂ ਨੂੰ ਨਾਮ ਦੇ ਸਕਦਾ ਹੈ। ਕਿਉਂਕਿ ਇਹ ਇੱਕ ਐਕਸਪ੍ਰੈਸ਼ਨ ਹੈ, ਇਹ ਸਵਭਾਵਿਕ ਤੌਰ 'ਤੇ ਨਤੀਜਾ ਉਤਪਾਦਦਾ ਹੈ—ਅਕਸਰ ਸੰਕੁਚਿਤ, ਪੜ੍ਹਨ-ਯੋਗ ਕੋਡ ਦੇ ਪੈਦਾ ਹੋਣ ਦਾ ਕਾਰਨ।
sealed trait Payment
case class Card(last4: String) extends Payment
case object Cash extends Payment
def describe(p: Payment): String = p match {
case Card(last4) => s"Card ending $last4"
case Cash => "Cash"
}
ਉਪਰੋਕਤ ਉਦਾਹਰਣ Scala ਦੇ ADT ਅੰਦਾਜ਼ ਦਾ ਵੀ ਦਿਖਾਉਂਦੀ ਹੈ:
sealed trait ਇੱਕ ਬੰਦ ਸੰਭਾਵਨਾਵਾਂ ਦਾ ਸੈੱਟ ਪਰਿਭਾਸ਼ਿਤ ਕਰਦਾ ਹੈ।case class ਅਤੇ case object ਐਸੇ ਕੰਕਰੀਟ ਵਰਇੰਟਸ ਨੂੰ ਪਰਿਭਾਸ਼ਿਤ ਕਰਦੇ ਹਨ।“Sealed” ਮਹੱਤਵਪੂਰਨ ਹੈ: ਕੰਪਾਇਲਰ ਨੂੰ ਸਾਰੇ ਵੈਧ ਸਬਟਾਇਪ ਪਤਾ ਹੁੰਦੇ ਹਨ (ਉਸੇ ਫਾਇਲ ਵਿੱਚ), ਜੋ ਸੁਰੱਖਿਅਤ pattern matching ਨੂੰ ਸੰਭਵ ਬਣਾਉਂਦਾ ਹੈ।
ADTs ਤੁਹਾਨੂੰ ਆਪਣੇ ਡੋਮੇਨ ਦੀਆਂ ਹਕੀਕਤੀ ਸਥਿਤੀਆਂ ਮਾਡਲ ਕਰਨ ਲਈ ਉਤਸ਼ਾਹਿਤ ਕਰਦੇ ਹਨ। null, ਜਾਦੂਈ ਸਤਰਾਂ ਜਾਂ ਅਜਿਹੇ booleans ਜੋ ਅਸੰਭਵ ਸਥਿਤੀਆਂ ਪੈਦਾ ਕਰ ਸਕਦੇ ਹਨ ਦੀ ਥਾਂ, ਤੁਸੀਂ ਵਿਧਾਨਤ ਰੂਪ ਵਿੱਚ ਮੰਨਯੋਗ ਕੇਸਾਂ ਨੂੰ ਪਰਿਭਾਸ਼ਿਤ ਕਰਦੇ ਹੋ। ਇਸ ਨਾਲ ਬਹੁਤ ਸਾਰੇ ਗਲਤੀਆਂ ਕੋਡ ਵਿੱਚ ਪ੍ਰਗਟ ਹੋਣ ਹੀ ਨਹੀਂ ਦਿੰਦੀਆਂ—ਇਸ ਲਈ ਉਹ ਪ੍ਰੋਡਕਸ਼ਨ ਵਿੱਚ ਨਹੀਂ ਫਸਦੀਆਂ।
Pattern matching ਉਸ ਸਮੇਂ ਚਮਕਦਾ ਹੈ ਜਦੋਂ ਤੁਸੀਂ:
ਜੇ ਹਰ ਜਗ੍ਹਾ ਵੱਡੇ match ਬਲੌਕ ਆ ਜਾਂਦੇ ਹਨ ਤਾਂ ਇਹ overused ਹੋ ਸਕਦਾ ਹੈ—ਇਸ صورت ਵਿੱਚ ਸਹਾਇਕ ਫੰਕਸ਼ਨਾਂ ਨਾਲ ਫੈਕਟਰਿੰਗ ਕਰਨ ਜਾਂ ਕੁਝ ਵਿਹਾਰ ਡਾਟਾ ਟਾਈਪ ਦੇ ਨੇੜੇ ਲਿਜਾਨ ਦੀ ਲੋੜ ਹੋ ਸਕਦੀ ਹੈ।
Scala ਦਾ ਟਾਈਪ ਸਿਸਟਮ ਉਹਨਾਂ ਵੱਡੀਆਂ ਵਜ੍ਹਾਂ ਵਿੱਚੋਂ ਇੱਕ ਹੈ ਜਿਸ ਕਰਕੇ ਟੀਮਾਂ ਇਸਨੂੰ ਚੁਣਦੀਆਂ ਹਨ—ਅਤੇ ਦਰਅਸਲ ਉਹ ਵਜ੍ਹਾਂ ਵੀ ਹਨ ਜਿਨ੍ਹਾਂ ਕਾਰਨ ਕੁਝ ਟੀਮਾਂ ਇਸਨੂੰ ਛੱਡ ਦਿੰਦੀਆਂ ਹਨ। ਵਧੀਆ ਹਾਲਤ ਵਿੱਚ, ਇਹ ਤੁਹਾਨੂੰ ਸੰਕੁਚਿਤ ਕੋਡ ਲਿਖਣ ਦਿੰਦਾ ਹੈ ਜੋ ਫਿਰ ਵੀ compile-ਟਾਈਮ ਚੈੱਕ ਹੁੰਦਾ ਹੈ। ਬੁਰੇ ਹਾਲਤ ਵਿੱਚ, ਇਹ ਐਸਾ ਮਹਿਸੂਸ ਕਰਵਾਂਦਾ ਹੈ ਜਿਵੇਂ ਤੁਸੀਂ ਕੰਪਾਇਲਰ ਨੂੰ ਡੀਬੱਗ ਕਰ ਰਹੇ ਹੋ।
ਟਾਈਪ ਇਨਫ਼ਰੇਂਸ ਦਾ ਮਤਲਬ ਹੈ ਕਿ ਤੁਹਾਨੂੰ ਆਮ ਤੌਰ 'ਤੇ ਹਰ ਥਾਂ ਟਾਈਪ ਨਾ ਲਿਖਨੀ ਪੈਂਦੀ। ਕੰਪਾਇਲਰ ਅਕਸਰ ਸੰਦਰਭ ਤੋਂ ਉਹਨਾਂ ਨੂੰ ਨਿਕਾਲ ਲੈ ਜਾਂਦਾ ਹੈ।
ਇਸ ਨਾਲ ਬੋਇਲਰਪਲੇਟ ਘੱਟ ਹੁੰਦੀ ਹੈ: ਤੁਸੀਂ ਇੱਕ ਮੁੱਲ ਕਿਸ ਗੱਲ ਦਾ ਪ੍ਰਤੀਕ ਹੈ ਇਸ 'ਤੇ ਧਿਆਨ ਕੇਂਦਰਿਤ ਕਰ ਸਕਦੇ ਹੋ ਨਾ ਕਿ ਹਰ ਸਥਾਨਕ ਵੈਰੀਏਬਲ ਲਈ ਟਾਈਪ ਲਿਖਣ 'ਤੇ। ਜਦੋਂ ਤੁਸੀਂ ਟਾਈਪ ਐਨੋਟੇਸ਼ਨ ਜੋੜਦੇ ਹੋ, ਤਾਂ ਅਕਸਰ ਇਹ ਸਰਹੱਦਾਂ (ਪਬਲਿਕ APIs, ਮੁੜ-ਜਟਿਲ ਜੇਨੇਰਿਕਸ) 'ਤੇ ਹੁੰਦਾ ਹੈ ਤਾ ਕਿ ਨੀਤੀ ਸਪੱਸ਼ਟ ਰਹੇ।
Generics ਤੁਹਾਨੂੰ ਕਨਟੇਨਰ ਅਤੇ ਯੂਟਿਲਿਟੀ ਲਿਖਣ ਦੀ ਆਜ਼ਾਦੀ ਦਿੰਦੀਆਂ ਹਨ ਜੋ ਕਈ ਟਾਈਪਾਂ ਲਈ ਕੰਮ ਕਰਦੀਆਂ ਹਨ (ਜਿਵੇਂ List[Int] ਅਤੇ List[String])। Variance ਇਸ ਗੱਲ ਦਾ ਨਿਯਮ ਹੈ ਕਿ ਜਦੋਂ ਉਸਦੀ ਟਾਈਪ ਪੈਰਾਮੀਟਰ ਬਦਲਦਾ ਹੈ ਤਾਂ ਜੈਨੇਰਿਕ ਟਾਈਪ ਨੂੰ substitute ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ ਕਿ ਨਹੀਂ।
+A) ਲਗਭਗ ਇਸਦਾ ਅਰਥ ਹੈ “cats ਦੀ ਸੂਚੀ ਨੂੰ animals ਦੀ ਸੂਚੀ ਦੀ ਥਾਂ ਵਰਤਿਆ ਜਾ ਸਕਦਾ ਹੈ।”-A) ਲਗਭਗ ਇਸਦਾ ਅਰਥ ਹੈ “animals ਦਾ ਹੈਂਡਲਰ cats ਦੇ ਹੈਂਡਲਰ ਵਜੋਂ ਵਰਤਿਆ ਜਾ ਸਕਦਾ ਹੈ।”ਇਹ ਲਾਇਬ੍ਰੇਰੀ ਡਿਜ਼ਾਈਨ ਲਈ ਸ਼ਕਤੀਸ਼ਾਲੀ ਹੈ, ਪਰ ਪਹਿਲੀ ਵਾਰੀ ਮਿਲਣ 'ਤੇ ਇਹ ਭੁੱਲਭੁੱਲੈਆ ਜਿਹਾ ਲੱਗ ਸਕਦਾ ਹੈ।
Scala ਨੇ ਇੱਕ ਐਸਾ ਪੈਟਰਨ ਲੋਕਪ੍ਰਿਯ ਕੀਤਾ ਜਿੱਥੇ ਤੁਸੀਂ ਕਿਸੇ ਟਾਈਪ ਵਿੱਚ ਬਿਨਾਂ ਉਸਨੂੰ ਸੋਧੇ ਹੋਰ ਵਿਹਾਰ ਜੋੜ ਸਕਦੇ ਹੋ—ਇਹ implicitly capabilities ਪਾਸ ਕਰਕੇ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਉਦਾਹਰਣ ਵਜੋਂ, ਤੁਸੀਂ ਕਿਸੇ ਟਾਈਪ ਨੂੰ ਕਿਵੇਂ compare ਜਾਂ print ਕਰਨਾ ਹੈ ਪਰਿਭਾਸ਼ਿਤ ਕਰ ਸਕਦੇ ਹੋ ਅਤੇ ਉਹ ਲਾਜ਼ਮੀ ਤੌਰ 'ਤੇ ਚੁਣਿਆ ਜਾਵੇਗਾ।
Scala 2 ਵਿੱਚ ਇਹ implicit ਨਾਲ ਹੁੰਦਾ ਸੀ; Scala 3 ਵਿੱਚ ਇਹ given/using ਨਾਲ ਹੋਰ ਸਪੱਸ਼ਟ ਤਰੀਕੇ ਨਾਲ ਦਿੱਤਾ ਗਿਆ ਹੈ। ਵਿਚਾਰ ਬਰਾਬਰ ਹੀ ਰਹਿੰਦਾ ਹੈ: ਵਿਹਾਰ ਨੂੰ ਕਾਂਪੋਜ਼ੇਬਲ ਤਰੀਕੇ ਨਾਲ ਵਧਾਓ।
ਟਰੇਡ-ਆਫ਼ ਜਟਿਲਤਾ ਹੈ। ਟਾਈਪ-ਲੇਵਲ ਤ੍ਰਿਕਸ ਲੰਬੇ error messages ਪੈਦਾ ਕਰ ਸਕਦੇ ਹਨ, ਅਤੇ ਬਹੁਤ ਜ਼ਿਆਦਾ abstractions ਵਾਲਾ ਕੋਡ ਨਵੇਂ ਆਏ ਲੋਕਾਂ ਲਈ ਪੜ੍ਹਨਾ ਮੁਸ਼ਕਲ ਹੋ ਸਕਦਾ ਹੈ। ਬਹੁਤ ਸਾਰੀਆਂ ਟੀਮਾਂ ਇੱਕ ਨਿਯਮ ਅਪਣਾਉਂਦੀਆਂ ਹਨ: ਟਾਈਪ ਸਿਸਟਮ ਨੂੰ APIs ਨੂੰ ਸਧਾਰਨ ਕਰਨ ਅਤੇ ਗਲਤੀਆਂ ਰੋਕਣ ਲਈ ਵਰਤੋ, ਪਰ ਐਸੇ ਡਿਜ਼ਾਈਨ ਤੋਂ ਬਚੋ ਜੋ ਹਰ ਕਿਸੇ ਨੂੰ ਕੰਪਾਇਲਰ ਵਰਗਾ ਸੋਚਣ ਲਈ ਮਜ਼ਬੂਰ ਕਰਨ।
Scala ਵਿੱਚ concurrent ਕੋਡ ਲਿਖਣ ਲਈ ਕਈ “ਲੇਨ” ਹਨ। ਇਹ ਲਾਭਦਾਇਕ ਹੈ—ਕਿਉਂਕਿ ਹਰ ਸਮੱਸਿਆ ਲਈ ਇੱਕੋ ਜਿਹਾ ਪੱਧਰ ਲੋੜੀਂਦਾ ਨਹੀਂ—ਪਰ ਇਹ ਵੀ ਮਤਲਬ ਹੈ ਕਿ ਟੀਮਾਂ ਨੂੰ ਧਿਆਨ ਨਾਲ ਚੁਣਨਾ ਚਾਹੀਦਾ ਹੈ ਕਿ ਉਹ ਕੀ ਅਪਣਾਉਂਦੀਆਂ ਹਨ।
ਕਈ JVM ਐਪਾਂ ਲਈ, Future ਕੰਮ ਨੂੰ concurrent ਤਰੀਕੇ ਨਾਲ ਚਲਾਣ ਅਤੇ ਨਤੀਜੇ ਕਾਂਪੋਜ਼ ਕਰਨ ਦਾ ਸਭ ਤੋਂ ਸਧਾਰਣ ਤਰੀਕਾ ਹੈ। ਤੁਸੀਂ ਕੰਮ ਸ਼ੁਰੂ ਕਰਦੇ ਹੋ, ਫਿਰ map/flatMap ਨਾਲ ਇੱਕ async workflow ਬਣਾਉਂਦੇ ਹੋ ਬਿਨਾਂ thread ਨੂੰ ਬਲਾਕ ਕੀਤੇ।
ਵਧੀਆ ਮਾਨਸਿਕ ਮਾਡਲ: Futures ਉਹਨਾਂ ਅਜ਼ਾਦ ਕੰਮਾਂ ਲਈ ਵਧੀਆ ਹਨ (API ਕਾਲ, ਡੇਟਾਬੇਸ ਕਵੈਰੀਆਂ, ਬੈਕਗ੍ਰਾਊਂਡ ਕੈਲਕੁਲੇਸ਼ਨ) ਜਿੱਥੇ ਤੁਸੀਂ ਨਤੀਜਿਆਂ ਨੂੰ ਜੋੜਣਾ ਅਤੇ ਫੇਲਿਊਰ ਨੂੰ ਇੱਕ ਥਾਂ ਸੰਭਾਲਣਾ ਚਾਹੁੰਦੇ ਹੋ।
Scala ਤੁਹਾਨੂੰ Future ਚੇਨ ਨੂੰ for-comprehensions ਨਾਲ ਹੋਰ ਲੀਨੀਅਰ ਅੰਦਾਜ਼ ਵਿੱਚ ਦਰਸਾਉਣ ਦਿੰਦਾ ਹੈ। ਇਹ ਨਵੇਂ concurrency ਪ੍ਰਿਮਿਟਿਵ ਨਹੀਂ ਜੋੜਦਾ, ਪਰ ਇਰਾਦਾ ਕਲੀਅਰ ਕਰਦਾ ਹੈ ਅਤੇ "callback nesting" ਘਟਾਉਂਦਾ ਹੈ।
ਟਰੇਡ-ਆਫ਼: ਇਸਨੂੰ ਅਜੇ ਵੀ ਗਲਤੀ ਨਾਲ ਬਲਾਕ ਕਰਨਾ ਆਸਾਨ ਹੈ (ਉਦਾਹਰਣ ਲਈ Future ਦੀ ਉਡੀਕ ਕਰਕੇ) ਜਾਂ execution context ਨੂੰ ਓਵਰਲੋਡ ਕਰਨਾ ਜੇ ਤੁਸੀਂ CPU-bound ਅਤੇ IO-bound ਕੰਮ ਨੂੰ ਵੱਖ-ਵੱਖ ਨਹੀਂ ਕਰਦੇ।
ਲੰਬੇ-ਚੱਲਣ ਵਾਲੇ ਪਾਈਪਲਾਈਨਾਂ—events, logs, ਡਾਟਾ ਪ੍ਰੋਸੈਸਿੰਗ—ਲਈ streaming ਲਾਇਬ੍ਰੇਰੀਆਂ (ਜਿਵੇਂ Akka/Pekko Streams, FS2, ਜਾਂ ਮਿਲਦੇ-ਜੁਲਦੇ) flow control 'ਤੇ ਧਿਆਨ ਦਿੰਦੀਆਂ ਹਨ। ਮੁੱਖ ਵਿਸ਼ੇਸ਼ਤਾ backpressure ਹੈ: ਜਦੋਂ consumers ਪਿੱਛੇ ਰਹਿ ਜਾਂਦੇ ਹਨ ਤਾਂ producers ਧੀਰੇ ਹੋ ਜਾਂਦੇ ਹਨ।
ਇਹ ਮਾਡਲ ਅਕਸਰ “ਸਿਰਫ਼ ਹੋਰ Futures ਪੈਦਾ ਕਰੋ” ਦੀ ਤਰਕ ਤੋਂ ਬਿਹਤਰ ਹੁੰਦਾ ਹੈ ਕਿਉਂਕਿ ਇਹ throughput ਅਤੇ memory ਨੂੰ ਪਹਿਲ-ਕਿੱਤੇ ਵਿਚਾਰ ਵਜੋਂ ਲੈਂਦਾ ਹੈ।
Actor ਲਾਇਬ੍ਰੇਰੀਆਂ (Akka/Pekko) concurrency ਨੂੰ ਐਸੇ ਆਜ਼ਾਦ ਕੰਪੋਨੈਂਟਾਂ ਵਜੋਂ ਮਾਡਲ ਕਰਦੀਆਂ ਹਨ ਜੋ ਸੁਨੇਹਿਆਂ ਰਾਹੀਂ ਗੱਲ ਕਰਦੇ ਹਨ। ਇਹ ਸਟੇਟ ਬਾਰੇ ਸੋਚਣਾ ਆਸਾਨ ਕਰ ਸਕਦਾ ਹੈ, ਕਿਉਂਕਿ ਹਰ actor ਇਕ-ਸਮੇਂ 'ਤੇ ਇੱਕ ਸੁਨੇਹਾ ਹੈਂਡਲ ਕਰਦਾ ਹੈ।
Actors ਉਹਨਾਂ ਹਾਲਤਾਂ ਵਿੱਚ ਚਮਕਦੇ ਹਨ ਜਿੱਥੇ ਤੁਹਾਨੂੰ ਲੰਬੇ ਸਮੇਂ ਵੱਲੇ, stateful ਪ੍ਰਕਿਰਿਆਵਾਂ (ਜਿਵੇਂ devices, sessions, coordinators) ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ। ਉਹ ਸਧਾਰਨ request/response ਐਪਾਂ ਲਈ ਬੇ-ਕਾਰਵਾਈ ਹੋ ਸਕਦੇ ਹਨ।
ਅਪਰਿਵਰਤੀ ਡਾਟਾ ਸਾਂਝੇ ਮਿਊਟੇਬਲ ਸਟੇਟ ਨੂੰ ਘਟਾਉਂਦੀ ਹੈ—ਇਹ ਬਹੁਤ ਸਾਰੀਆਂ ਰੇਸ-ਕੰਡਿਸ਼ਨਾਂ ਦਾ ਸਰੋਤ ਹੈ। ਭਾਵੇਂ ਤੁਸੀਂ threads, Futures, ਜਾਂ actors ਵਰਤੋਂ, ਅਪਰਿਵਰਤੀ ਮੁੱਲ ਭੇਜਣਾ concurrency ਬੱਗਾਂ ਨੂੰ ਘੱਟ ਅਤੇ ਡੀਬੱਗਿੰਗ ਨੂੰ ਅਸਾਨ ਬਣਾਂਦਾ ਹੈ।
ਸਧਾਰਣ ਪੱਧਰ 'ਤੇ Futures ਨਾਲ ਸ਼ੁਰੂ ਕਰੋ। ਜਦੋਂ throughput ਨਿਯੰਤਰਣ ਅਤੇ ਸੰਸਾਧਨ-ਸੰਬੰਧੀ ਮਸਲੇ ਆਉਣ ਤਾਂ streaming 'ਤੇ ਪਰਵਾਨ ਚੜ੍ਹੋ, ਅਤੇ ਜਦੋਂ ਸਟੇਟ ਅਤੇ ਕੋਆਰਡੀਨੇਸ਼ਨ ਮੁੱਖ ਬਣ ਜਾਵੇ ਤਾਂ actors ਦੀ ਸੋਚੋ।
Scala ਦੀ ਸਭ ਤੋਂ ਵੱਡੀ ਪ੍ਰਯੋਗਿਕ ਫ਼ਾਇਦਾ ਇਹ ਹੈ ਕਿ ਇਹ JVM 'ਤੇ ਰਹਿੰਦੀ ਹੈ ਅਤੇ Java ਪਰਿਵਾਰ ਨੂੰ ਸਿੱਧਾ ਵਰਤ ਸਕਦੀ ਹੈ। ਤੁਸੀਂ Java ਕਲਾਸਾਂ ਨੂੰ instantaneous ਤਰੀਕੇ ਨਾਲ instantiate ਕਰ ਸਕਦੇ ਹੋ, Java ਇੰਟਰਫੇਸ ਨੂੰ implement ਕਰ ਸਕਦੇ ਹੋ, ਅਤੇ Java ਮੈਥਡਸ ਨੂੰ ਬਿਨਾਂ ਜ਼ਿਆਦਾ ceremony ਦੇ ਕਾਲ ਕਰ ਸਕਦੇ ਹੋ—ਅਕਸਰ ਇਹ ਮਹਿਸੂਸ ਹੁੰਦਾ ਹੈ ਜਿਵੇਂ ਤੁਸੀਂ ਸਿਰਫ਼ ਹੋਰ ਇੱਕ Scala ਲਾਇਬ੍ਰੇਰੀ ਵਰਤ ਰਹੇ ਹੋ।
ਜ਼ਿਆਦਾਤਰ “ਖੁਸ਼ੀ-ਰਾਹ” interop ਸਿੱਧਾ ਹੈ:
ਅੰਦਰੋਂ, Scala JVM ਬਾਈਟਕੋਡ 'ਚ ਕੰਪਾਇਲ ਹੁੰਦੀ ਹੈ। ਚਲਾਉਣ ਵਾਲੀ ਦ੍ਰਿਸ਼ਟੀ ਤੋਂ, ਇਹ ਹੋਰ JVM ਭਾਸ਼ਾਵਾਂ ਵਰਗੀ ਹੀ ਚੱਲਦੀ ਹੈ: ਉਹੀ runtime ਪ੍ਰਬੰਧਨ, ਉਹੀ GC, ਅਤੇ ਉਹੇ profiling/monitoring ਟੂਲ ਵਰਤੇ ਜਾ ਸਕਦੇ ਹਨ।
ਉਹ ਝਕੜ ਜਾਂਦਾ ਹੈ ਜਦੋਂ Scala ਦੇ ਡਿਫੌਲਟ Java ਨਾਲ ਮੇਲ ਨਹੀਂ ਖਾਂਦੇ:
Nulls. ਬਹੁਤ ਸਾਰੀਆਂ Java APIs null ਵਾਪਸ ਕਰਦੀਆਂ ਹਨ; Scala ਕੋਡ Option ਨੂੰ ਤਰਜੀਹ ਦਿੰਦਾ ਹੈ। ਤੁਹਾਨੂੰ ਅਕਸਰ Java ਨਤੀਜਿਆਂ ਨੂੰ defensive ਤਰੀਕੇ ਨਾਲ ਲਪੇਟਣਾ ਪੈਂਦਾ ਹੈ ਤਾਂ ਕਿ ਅਚਾਨਕ NullPointerException ਨਾ ਆਵੇ।
Checked exceptions. Scala ਤੁਹਾਨੂੰ checked exceptions ਦੀ ਘੋਸ਼ਣਾ ਕਰਨ ਜਾਂ ਕੈਚ ਕਰਨ 'ਤੇ ਮਜ਼ਬੂਰ ਨਹੀਂ ਕਰਦੀ, ਪਰ Java ਲਾਇਬ੍ਰੇਰੀਆਂ ਫਿਰ ਵੀ ਉਹ ਐਕਸਪਾਵਟ ਕਰ ਸਕਦੀਆਂ ਹਨ। ਇਹ ਐਰਰ ਹੈਂਡਲਿੰਗ ਨੂੰ inconsistent ਮਹਿਸੂਸ ਕਰਵਾ ਸਕਦਾ ਹੈ ਜੇ ਤੁਸੀਂ ਨਹੀਂ ਨੀਤੀ ਤਿਆਰ ਕਰਦੇ ਕਿ exceptions ਨੂੰ ਕਿਵੇਂ translate ਕਰਨਾ ਹੈ।
Mutability. Java collections ਅਤੇ setter-heavy APIs mutation ਨੂੰ ਉਤਸ਼ਾਹਿਤ ਕਰਦੇ ਹਨ। Scala ਵਿੱਚ mutable ਅਤੇ immutable ਸ਼ੈਲੀਆਂ ਨੂੰ ਮਿਲਾਉਣਾ confounding ਹੋ ਸਕਦਾ ਹੈ, ਖਾਸ ਕਰਕੇ API ਹੱਦਾਂ 'ਤੇ।
ਹੱਦ ਨੂੰ ਇੱਕ translation layer ਵਜੋਂ ਟ੍ਰੀਟ ਕਰੋ:
Option ਵਿੱਚ ਬਦਲੋ, ਅਤੇ ਕੇਵਲ ਹੱਦ 'ਤੇ Option ਨੂੰ null ਵਿੱਚ ਵਾਪਸ ਬਦਲੋ।ਚੰਗੀ ਤਰ੍ਹਾਂ ਕੀਤਾ ਗਿਆ, ਇੰਟਰਓਪ Scala ਟੀਮਾਂ ਨੂੰ ਤੇਜ਼ੀ ਨਾਲ Proven JVM ਲਾਇਬ੍ਰੇਰੀਆਂ ਰੀਯੂਜ਼ ਕਰਨ ਦੇ ਯੋਗ ਬਣਾ ਦਿੰਦਾ ਹੈ ਜਦੋਂ ਕਿ ਸੇਵਾ ਦੇ ਅੰਦਰ Scala ਕੋਡ ਨੂੰ expressive ਅਤੇ ਸੁਰੱਖਿਅਤ ਰੱਖਦਾ ਹੈ।
Scala ਦਾ ਪ੍ਰਸਤਾਵ ਆਕਰਸ਼ਕ ਹੈ: ਤੁਸੀਂ ਸੁੰਦਰ ਫੰਕਸ਼ਨਲ ਕੋਡ ਲਿਖ ਸਕਦੇ ਹੋ, ਜਿੱਥੇ OO ਰਚਨਾ ਲੋੜੀਂਦੀ ਹੋਵੈਂ ਉੱਥੇ ਰੱਖੋ, ਅਤੇ JVM 'ਤੇ ਹੀ ਰਹੋ। ਪਰ ਅਮਲ ਵਿੱਚ, ਟੀਮਾਂ ਸਿਰਫ਼ "Scala ਪ੍ਰਾਪਤ" ਨਹੀਂ ਕਰਦੀਆਂ—ਉਨ੍ਹਾਂ ਨੂੰ ਦਿਨ-ਪ੍ਰਤੀ-ਦਿਨ ਕੁਝ ਟਰੇਡ-ਆਫ਼ਸ ਮਹਿਸੂਸ ਹੁੰਦੀਆਂ ਹਨ ਜੋ ਅਨਬੋਰਡਿੰਗ, ਬਿਲਡ ਅਤੇ ਕੋਡ ਰਿਵਿਊਜ਼ ਵਿੱਚ ਸਾਹਮਣੇ ਆਉਂਦੀਆਂ ਹਨ।
Scala ਤੁਹਾਨੂੰ ਬਹੁਤ ਸਾਰੀ ਅਭਿਵ્યਕਤੀ ਸ਼ਕਤੀ ਦਿੰਦਾ ਹੈ: ਡੇਟਾ ਮਾਡਲ ਕਰਨ ਦੇ ਕਈ ਤਰੀਕੇ, ਵਿਹਾਰ ਐਬਸਟ੍ਰੈਕਟ ਕਰਨ ਦੇ ਕਈ ਤਰੀਕੇ, APIs ਸੰਰਚਨਾ ਕਰਨ ਦੇ ਕਈ ਤਰੀਕੇ। ਜਦੋਂ ਤੁਸੀਂ ਇੱਕ ਸਾਂਝਾ ਮਾਨਸਿਕ ਮਾਡਲ ਸਾਂਝਾ ਕਰ ਲੈਂਦੇ ਹੋ ਤਾਂ ਇਹ ਉਤਪਾਦਕ ਹੈ—ਪਰ ਸ਼ੁਰੂਆਤੀ दौर 'ਚ ਇਹ ਟੀਮਾਂ ਨੂੰ ਹੌਲਾ ਕਰ ਸਕਦਾ ਹੈ।
ਨਵੇਂ ਆਏ ਲੋਕਾਂ ਨੂੰ Syntax ਨਾਲੋਂ ਜ਼ਿਆਦਾ ਚਿੰਤਾ Choice ਨਾਲ ਹੁੰਦੀ ਹੈ: “ਇਹ case class ਹੋਵੇ, normal class, ਜਾਂ ADT?” “ਅਸੀਂ inheritance, traits, type classes, ਜਾਂ ਸਿਰਫ਼ ਫੰਕਸ਼ਨ ਵਰਤਾਂ?” ਕਠਿਨਾਈ ਇਹ ਨਹੀਂ ਕਿ Scala ਅਸੰਭਵ ਹੈ—ਸਗੋਂ ਇਹ ਹੈ ਕਿ ਟੀਮ ਨੂੰ ਇਹ ਫੈਸਲਾ ਕਰਨ ਦੀ ਲੋੜ ਹੈ ਕਿ ਉਹਨਾਂ ਲਈ “ਸਧਾਰਣ Scala” ਕੀ ਹੈ।
Scala ਕੰਪਾਈਲੇਸ਼ਨ ਜ਼ਿਆਦਾ ਭਾਰੀ ਹੋ ਸਕਦੀ ਹੈ, ਖਾਸ ਕਰਕੇ ਜਦੋਂ ਪ੍ਰੋਜੈਕਟ ਵੱਡੇ ਹੋ ਜਾਂਦੇ ਹਨ ਜਾਂ macro-heavy ਲਾਇਬ੍ਰੇਰੀਆਂ ਤੇ ਨਿਰਭਰ ਹੁੰਦੇ ਹਨ (Scala 2 ਵਿੱਚ ਇਹ ਜ਼ਿਆਦਾ ਆਮ). Incremental builds ਮਦਦ ਕਰ ਸਕਦੀਆਂ ਹਨ, ਪਰ compile time ਇੱਕ ਮੁਕਾਬਲਾ ਰਹਿੰਦੀ ਹੈ: CI ਧੀਮਾ, ਫੀਡਬੈਕ ਲੂਪ ਸੁਸਤ, ਅਤੇ ਮਾਡਿਊਲਾਂ ਨੂੰ ਛੋਟਾ ਅਤੇ dependencies ਸਾਫ਼ ਰੱਖਣ 'ਤੇ ਦਬਾਅ।
ਬਿਲਡ ਟੂਲ ਵੀ ਇੱਕ ਹੋਰ ਪਰਤ ਜੋੜਦੇ ਹਨ। sbt ਜਾਂ ਹੋਰ ਬਿਲਡ ਸਿਸਟਮ ਵਰਤੋਂ, ਤਾਂ ਤੁਹਾਨੂੰ caching, parallelism, ਅਤੇ ਪ੍ਰੋਜੇਕਟ ਦੇ ਸਬ-ਮੋਡੀਊਲਾਂ ਦੀ ਵਿਭਾਜ਼ਨ ਤੇ ਧਿਆਨ ਦੇਣਾ ਪਏਗਾ। ਇਹ ਸਿਧੇ-ਸਾਧੇ ਮਸਲੇ ਨਹੀਂ—ਉਹ ਡਿਵੈਲਪਰ ਖੁਸ਼ੀ ਅਤੇ bug fix ਦੀ ਰਫ਼ਤਾਰ 'ਤੇ ਅਸਰ ਪਾਉਂਦੇ ਹਨ।
Scala ਟੂਲਿੰਗ ਵਿੱਚ ਕਾਫ਼ੀ ਸੁਧਾਰ ਆਇਆ ਹੈ, ਪਰ ਇਹ ਹੁਣ ਵੀ ਤੁਹਾਡੇ ਖਾਸ ਸਟੈਕ ਨਾਲ ਟੈਸਟ ਕਰਨ ਯੋਗ ਹੈ। ਮਿਆਰੀਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਟੀਮਾਂ ਨੂੰ ਇਹਨਾਂ ਚੀਜ਼ਾਂ ਦੀ ਪੜਤਾਲ ਕਰਨੀ ਚਾਹੀਦੀ ਹੈ:
ਜੇ IDE ਸੰਘਰਸ਼ ਕਰੇ, ਤਾਂ ਭਾਸ਼ਾ ਦੀ expressiveness ਬਖ਼਼ਿਆਲ ਵਾਕਿਆ ਬਦਤਰ ਹੋ ਸਕਦੀ ਹੈ: "ਸਹੀ" ਪਰ ਖੋਜਣ ਵਿੱਚ ਮੁਸ਼ਕਲ ਕੋਡ ਮਹਿੰਗਾ ਬਣ ਜਾਂਦਾ ਹੈ।
ਕਿਉਂਕਿ Scala FP ਅਤੇ OO (ਅਤੇ ਬਹੁਤ ਸਾਰੇ ਹਾਈਬ੍ਰਿਡ) ਨੂੰ ਸਹਾਰਦਾ ਹੈ, ਟੀਮਾਂ ਇੱਕ ਐਸੇ ਕੋਡਬੇਸ ਵਿੱਚ ਫਸ ਸਕਦੀਆਂ ਹਨ ਜੋ ਕਈ ਭਾਸ਼ਾਵਾਂ ਵਰਗਾ ਮਹਿਸੂਸ ਹੁੰਦਾ ਹੈ। ਇਹ ਆਮ ਤੌਰ 'ਤੇ Scala ਦੇ ਕਾਰਨ ਨਹੀਂ ਹੁੰਦਾ, ਪਰ inconsistent conventions ਕਾਰਨ ਹੁੰਦਾ ਹੈ।
ਨਿਯਮ ਅਤੇ linters ਮਹੱਤਵਪੂਰਨ ਹਨ ਕਿਉਂਕਿ ਉਹ ਵਾਦ-ਵਿਵਾਦ ਘਟਾਉਂਦੇ ਹਨ। ਪਹਿਲਾਂ ਤੈਅ ਕਰੋ ਕਿ ਤੁਹਾਡੀ ਟੀਮ ਲਈ “ਵਧੀਆ Scala” ਕੀ ਹੈ—ਤੁਸੀਂ ਅਪਰਿਵਰਤੀਤਾ, ਏਰਰ ਹੈਂਡਲਿੰਗ, ਨਾਮਕਰਨ ਅਤੇ advanced type-level patterns ਨੂੰ ਕਿਵੇਂ ਹਂਡਲ ਕਰਦੇ ਹੋ। consistency onboarding ਨੂੰ ਸੁਗਮ ਬਣਾਉਂਦੀ ਹੈ ਅਤੇ ਰਿਵਿਊਜ਼ ਨੂੰ ਵਿਹਾਰ ਬਾਰੇ ਹੀ ਕੇਂਦਰਿਤ ਰੱਖਦੀ ਹੈ, ਨਾ ਕਿ ਸਜਾਵਟ ਬਾਰੇ।
Scala 3 (ਜੋ ਵਿਕਾਸ ਦੌਰਾਨ "Dotty" ਕਹਾਇਆ ਜਾਂਦਾ ਸੀ) Scala ਦੀ ਪਛਾਣ ਨੂੰ ਦੁਬਾਰਾ ਨਹੀਂ ਲਿਖਦਾ—ਇਹ Scala 2 ਵਿੱਚ ਟੀਮਾਂ ਨੂੰ ਜੋ ਧਾਰੀਆਂ ਲੱਗੀਆਂ ਉਹਨਾਂ ਦੇ ਕਾਂਟੇ ਗੋਲ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰਦਾ ਹੈ।
Scala 3 ਪਰਿਚਿਤ ਮੂਲ ਰੱਖਦੀ ਹੈ, ਪਰ ਕੋਡ ਨੂੰ ਸਾਫ਼ ਸੰਗਠਿਤ ਕਰਨ ਵੱਲ ਧੱਕਦੀ ਹੈ।
ਤੁਸੀਂ optional braces ਅਤੇ significant indentation ਨੂੰ ਵੇਖੋਗੇ, ਜੋ ਰੋਜ਼ਾਨਾ ਕੋਡ ਨੂੰ ਇੱਕ ਆਧੁਨਿਕ ਭਾਸ਼ਾ ਵਾਂਗ ਪੜ੍ਹਨਯੋਗ ਬਣਾਉਂਦਾ ਹੈ ਅਤੇ DSL-ਜਿਹਾ ਭਾਵ ਘੱਟ ਕਰਦਾ ਹੈ। ਇਹ ਕੁਝ ਪੈਟਰਨਾਂ ਨੂੰ ਵੀ ਮਿਆਰੀਕਰਦਾ ਹੈ ਜੋ Scala 2 ਵਿੱਚ “ਸੰਭਵ ਪਰ ਗੰਦ” ਸਨ—ਜਿਵੇਂ extension ਦੁਆਰਾ methods ਜੋੜਨਾ, ਜਿਸ ਦੀ ਥਾਂ implicit ਚਾਲਾਕੀਆਂ।
ਦਾਰਸ਼ਨਿਕ ਰੂਪ ਵਿੱਚ, Scala 3 ਕੋਸ਼ਿਸ਼ ਕਰਦੀ ਹੈ ਕਿ ਸ਼ਕਤੀਸ਼ਾਲੀ ਫੀਚਰ ਜ਼ਿਆਦਾ ਸਪੱਸ਼ਟ ਮਹਿਸੂਸ ਹੋਣ ਤਾਂ ਕਿ ਪੜ੍ਹਨ ਵਾਲਾ ਮਹਿਸੂਸ ਕਰ ਸਕੇ ਕਿ ਕੀ ਹੋ ਰਿਹਾ ਹੈ ਬਿਨਾਂ ਦਸਕਿਆ ਹੋਇਆ ਬਹੁਤ ਸਾਰੇ conventions ਨੂੰ ਯਾਦ ਕਰਨ ਦੇ।
Scala 2 ਦੇ implicits ਬਹੁਤ ਲਚਕੀਲੇ ਸਨ: typeclasses ਅਤੇ dependency injection ਲਈ ਮਹਾਨ, ਪਰ ਇਹ confusing compile errors ਅਤੇ “action at a distance” ਦਾ ਸਰੋਤ ਵੀ ਸਨ।
Scala 3 ਨੇ ਜ਼ਿਆਦਾਤਰ implicit ਵਰਤੋਂ given/using ਨਾਲ ਬਦਲੀ। ਯੋਗਤਾ ਮੁਕਾਬਲਾ ਉਹੀ ਰਹਿੰਦੀ ਹੈ, ਪਰ ਇਰਾਦਾ ਸਪੱਸ਼ਟ ਹੁੰਦਾ ਹੈ: “ਇੱਥੇ ਇਕ ਦਿੱਤੀ ਜਾ ਰਹੀ ਇਕਾਈ ਹੈ” (given) ਅਤੇ “ਇਸ ਮੈਥਡ ਨੂੰ ਇੱਕ ਚਾਹੀਦਾ ਹੈ” (using)। ਇਸ ਨਾਲ readability ਸੁਧਰਦੀ ਹੈ ਅਤੇ FP-ਸਟਾਈਲ typeclass ਪੈਟਰਨ ਆਮ ਤੌਰ 'ਤੇ ਫੋਲੋ ਕਰਨ ਲਈ ਆਸਾਨ ਹੋ ਜਾਂਦੇ ਹਨ।
Enums ਵੀ ਮਹੱਤਵਪੂਰਣ ਬਦਲਾਅ ਹਨ। ਬਹੁਤ Scala 2 ਟੀਮਾਂ sealed traits + case objects/classes ਨਾਲ ADTs ਮਾਡਲ ਕਰਦੀਆਂ ਸਨ। Scala 3 ਦਾ enum ਤੁਹਾਨੂੰ ਉਹੀ ਪੈਟਰਨ ਇੱਕ ਸਮਰੱਥ, ਸਾਫ਼ syntax ਨਾਲ ਦਿੰਦਾ ਹੈ—ਘੱਟ ਬੋਇਲਰਪਲੇਟ, ਉਹੀ ਮਾਡਲਿੰਗ ਸ਼ਕਤੀ।
ਅਕਸਰ ਅਸਲ ਪ੍ਰੋਜੈਕਟ cross-building (Scala 2 ਅਤੇ Scala 3 ਦੋਹਾਂ ਆਰਟੀਫੈਕਟ ਪਬਲਿਸ਼) ਕਰਕੇ ਅਤੇ ਮੋਡੀਊਲ-ਦਰ-ਮੋਡੀਊਲ ਮਾਈਗਰੇਟ ਕਰਕੇ ਅੱਗੇ ਵਧਦੀਆਂ ਹਨ।
ਟੂਲ ਮਦਦ ਕਰਦੇ ਹਨ, ਪਰ ਫਿਰ ਵੀ ਇਹ ਕੰਮ ਹੈ: source incompatibilities (ਖਾਸ ਕਰਕੇ implicits ਦੇ ਆਸਪੈਕਟ), macro-heavy ਲਾਇਬ੍ਰੇਰੀਆਂ, ਅਤੇ ਬਿਲਡ ਟੂਲਿੰਅੰਗ ਸੁਧਾਰਨਾਂ ਤੁਹਾਨੂੰ ਹੌਲੀ ਕਰ ਸਕਦੀਆਂ ਹਨ। ਚੰਗੀ ਗੱਲ ਇਹ ਹੈ ਕਿ ਆਮ ਬਿਜਨਸ ਕੋਡ ਆਮ ਤੌਰ 'ਤੇ ਆਸਾਨੀ ਨਾਲ ਪੋਰਟ ਹੁੰਦਾ ਹੈ ਉਸ ਕੋਡ ਨਾਲ ਤੁਲਨਾ ਵਿੱਚ ਜੋ ਕੰਪਾਇਲਰ-ਜਾਦੂ 'ਤੇ ਭਾਰੀ ਰਿਹਾ ਹੈ।
ਰੋਜ਼ਾਨਾ ਕੋਡ ਵਿੱਚ, Scala 3 FP ਪੈਟਰਨਾਂ ਨੂੰ ਜ਼ਿਆਦਾ "ਪਹਿਲੇ ਦਰਜੇ" ਮਹਿਸੂਸ ਕਰਵਾਂਦਾ ਹੈ: typeclass ਵਾਇਰਿੰਗ ਸਪੱਸ਼ਟ, enums ਨਾਲ ਸੁੰਦਰ ADTs, ਅਤੇ ਤਾਕਤਵਰ ਟਾਈਪਿੰਗ ਟੂਲ (ਜਿਵੇਂ union/intersection types) ਘੱਟ ceremony ਦੇ ਨਾਲ।
ਇਸਦੇ ਨਾਲ-ਨਾਲ, ਇਹ OO ਨੂੰ ਛੱਡਦਾ ਨਹੀਂ—traits, classes, ਅਤੇ mixin composition ਕੰਮ ਬਣੇ ਰਹਿੰਦੇ ਹਨ। ਫਰਕ ਇਹ ਹੈ ਕਿ Scala 3 OO ਢਾਂਚੇ ਅਤੇ FP ਅਬਸਟ੍ਰੈਕਸ਼ਨਾਂ ਵਿਚਕਾਰ ਦੀਆਂ ਹੱਦਾਂ ਨੂੰ ਆਸਾਨੀ ਨਾਲ ਵੇਖਾਉਂਦਾ ਹੈ, ਜੋ ਆਮ ਤੌਰ 'ਤੇ ਟੀਮਾਂ ਨੂੰ ਕੋਡਬੇਸ ਨੂੰ ਸਮੇਂ ਦੇ ਨਾਲ ਇਕਸਾਰ ਰੱਖਣ ਵਿੱਚ ਮਦਦ ਕਰਦਾ ਹੈ।
Scala JVM 'ਤੇ ਇੱਕ ਸ਼ਕਤੀਸ਼ਾਲੀ "ਟੂਲ" ਭਾਸ਼ਾ ਹੋ ਸਕਦੀ ਹੈ—ਪਰ ਇਹ ਹਰ ਸਥਿਤੀ ਲਈ ਡਿਫੌਲਟ ਨਹੀਂ। ਸਭ ਤੋਂ ਵੱਡੇ ਫਾਇਦੇ ਉਹਨਾਂ ਸਮੱਸਿਆਵਾਂ ਵਿੱਚ ਨਜ਼ਰ ਆਉਂਦੇ ਹਨ ਜਿੱਥੇ ਮਜ਼ਬੂਤ ਮਾਡਲਿੰਗ ਅਤੇ ਸੁਰੱਖਿਅਤ ਰਚਨਾ ਲਈ ਲਾਭ ਹੈ, ਅਤੇ ਜਿੱਥੇ ਟੀਮ ਭਾਸ਼ਾ ਨੂੰ ਇਰਾਦੇ ਨਾਲ ਵਰਤਣ ਲਈ ਤਿਆਰ ਹੋ।
ਡਾਟਾ-ਭਾਰੀ ਸਿਸਟਮ ਅਤੇ ਪਾਈਪਲਾਈਨ। ਜੇ ਤੁਸੀਂ ਬਹੁਤ ਸਾਰਾ ਡੇਟਾ ਤਬਦੀਲ, ਵੈਰੀਫਾਈ ਅਤੇ ਇਨਰਚਾਂਜ ਕਰ ਰਹੇ ਹੋ (streams, ETL jobs, event processing), Scala ਦੀ ਫੰਕਸ਼ਨਲ ਸ਼ੈਲੀ ਅਤੇ ਮਜ਼ਬੂਤ ਟਾਈਪਸ ਉਹਨਾਂ ਤਬਦੀਲੀਆਂ ਨੂੰ ਸਪੱਸ਼ਟ ਅਤੇ ਘਟਤ-ਤ੍ਰੁੱਟੀ ਵਾਲੀਆਂ ਰੱਖਣ ਵਿੱਚ ਮਦਦ ਕਰਦੀਆਂ ਹਨ।
ਜਟਿਲ ਡੋਮੇਨ ਮਾਡਲਿੰਗ। ਜਦੋਂ ਬਿਜਨਸ ਨਿਯਮ ਨੁਕਸਾਨਯੋਗ ਹੁੰਦੇ ਹਨ—pricing, risk, eligibility, permissions—Scala ਦੀ ਟਾਈਪਿੰਗ ਅਤੇ ਛੋਟੇ, ਕਾਂਪੋਜ਼ੇਬਲ ਹਿੱਸਿਆਂ ਨੂੰ ਬਣਾਉਣ ਦੀ ਸਮਰੱਥਾ "if-else ਵਧਾਈ" ਨੂੰ ਘਟਾ ਸਕਦੀ ਹੈ ਅਤੇ ਗਲਤ ਸਥਿਤੀਆਂ ਦਾ ਪ੍ਰਗਟਾਵਾ ਮੁਸ਼ਕਲ ਕਰ ਸਕਦੀ ਹੈ।
JVM ਵਿੱਚ ਨਿਵੇਸ਼ਤ ਸੰਗਠਨ। ਜੇ ਤੁਹਾਡਾ ਪਰਿਵਾਰ ਪਹਿਲਾਂ ਹੀ Java ਲਾਇਬ੍ਰੇਰੀਆਂ, JVM ਟੂਲਿੰਗ, ਅਤੇ operational ਅਭਿਆਸਾਂ 'ਤੇ ਨਿਰਭਰ ਹੈ, ਤਾਂ Scala FP-ਸ਼ੈਲੀ ਵਰਤੋਂ ਵਿੱਚ ਆਰਾਮ ਦੇ ਸਕਦੀ ਹੈ ਬਿਨਾਂ ਉਸ ecosystem ਨੂੰ ਛੱਡੇ।
Scala consistency ਨੂੰ ਇਨਾਮ ਦਿੰਦੀ ਹੈ। ਟੀਮਾਂ ਆਮ ਤੌਰ 'ਤੇ ਤਦ ਹੀ ਸਫਲ ਹੁੰਦੀਆਂ ਹਨ ਜਦੋਂ ਉਨ੍ਹਾਂ ਕੋਲ:
ਇਨ੍ਹਾਂ ਦੇ ਬਿਨਾਂ, ਕੋਡਬੇਸ ਵਿਭਿੰਨ ਸ਼ੈਲੀਆਂ ਵੱਲ ਡ੍ਰਿਫਟ ਕਰ ਸਕਦਾ ਹੈ ਜੋ ਨਵੇਂ ਆਏ ਲੋਕਾਂ ਲਈ ਮੁਸ਼ਕਲ ਹੈ।
ਛੋਟੀ ਟੀਮਾਂ ਜਿਨ੍ਹਾਂ ਨੂੰ ਤੇਜ਼ onboarding ਚਾਹੀਦਾ ਹੈ। ਜੇ ਤੁਸੀਂ ਅਕਸਰ people ਹੇਅੱਡੋਫ-ਹੈਂਡਆਫ, ਬਹੁਤ ਸਾਰੇ ਜੂਨੀਅਰ ਯੋਗਦਾਤਾ, ਜਾਂ ਤੇਜ਼ ਸਟਾਫਿੰਗ-ਬਦਲਾਅ ਦੀ ਉਮੀਦ ਰੱਖਦੇ ਹੋ, ਤਾਂ ਸਿੱਖਣ-ਕਰਵ ਅਤੇ idioms ਦੀ ਵੈਰੀਐਟੀ ਤੁਹਾਨੂੰ ਢੀਲਾ ਕਰ ਸਕਦੀ ਹੈ।
ਸਿਰਫ਼ ਸਾਦਾ CRUD ਐਪਸ. ਜੇ ਤੁਹਾਡੇ ਸਰਵਿਸ ਸਧਾਰਨ “request in / record out” ਦੇ ਹਨ ਜਿੱਥੇ ਡੋਮੇਨ ਕੰਪਲੈਕਸਿਟੀ ਘੱਟ ਹੈ, Scala ਦੇ ਫਾਇਦੇ ਉਸ ਦੇ ਬਿਲਡ ਟੂਲਿੰਗ, ਕੰਪਾਈਲ ਸਮੇਂ, ਅਤੇ ਸ਼ੈਲੀ ਫaisਲਿਆਂ ਦੇ ਹੁਕਮ-ਲਾਗੂ ਕਰਨ ਦੀ ਕੀਮਤ ਨਹੀਂ ਵਜਾਇਦ ਹੋ ਸਕਦੇ।
ਪੂਛੋ:
ਜੇ ਤੁਸੀਂ ਬਹੁਤ ਸਾਰੇ “ਹਾਂ” ਕਹਿੰਦੇ ਹੋ, ਤਾਂ Scala ਅਕਸਰ ਇੱਕ ਮਜ਼ਬੂਤ ਫ਼ਿੱਟ ਹੁੰਦੀ ਹੈ। ਨਹੀਂ ਤਾਂ ਕੋਈ ਸਧਾਰਨ JVM ਭਾਸ਼ਾ ਜ਼ਿਆਦਾ ਤੇਜ਼ ਨਤੀਜੇ ਦੇ ਸਕਦੀ ਹੈ।
ਇੱਕ عملي ਸੁਝਾਅ ਜਦੋਂ ਤੁਸੀਂ ਭਾਸ਼ਾਵਾਂ ਦਾ ਮੁਲਾਂਕਣ ਕਰ ਰਹੇ ਹੋ: ਪ੍ਰੋਟੋਟਾਈਪ ਲੂਪ ਨੂੰ ਛੋਟਾ ਰੱਖੋ। ਉਦਾਹਰਣ ਲਈ, ਟੀਮਾਂ ਕਈ ਵਾਰੀ ਇੱਕ vibe-coding ਪਲੇਟਫਾਰਮ ਵਰਗਾ Koder.ai ਵਰਤ ਕੇ ਛੋਟਾ ਰੈਫਰੰਸ ਐਪ (API + ਡੇਟਾਬੇਸ + UI) ਚੈਟ-ਆਧਾਰਿਤ spec ਤੋਂ ਤੇਜ਼ੀ ਨਾਲ ਬਣਾਂਦੀਆਂ ਹਨ, ਯੋਜਨਾ ਮੋਡ ਵਿੱਚ ਇਤਰਾਏਨਟ ਕਰਦੀਆਂ ਹਨ, ਅਤੇ snapshots/rollback ਨਾਲ ਵਿਕਲਪਾਂ ਨੂੰ ਸੁਰੱਖਿਅਤ ਤਰੀਕੇ ਨਾਲ ਅਜ਼ਮਾਉਂਦੀਆਂ ਹਨ। ਚਾਹੇ ਤੁਹਾਡਾ ਪ੍ਰੋਡਕਸ਼ਨ ਟਾਰਗੇਟ Scala ਹੀ ਹੋਵੇ, ਇੱਕ ਤੇਜ਼ ਪ੍ਰੋਟੋਟਾਈਪ ਜਿਸਨੂੰ ਤੁਸੀਂ ਸਰੋਤ ਕੋਡ ਵਜੋਂ ਨਿਰਯਾਤ ਕਰ ਸਕਦੇ ਹੋ, JVM ਇੰਪਲਿਮੈਂਟੇਸ਼ਨਾਂ ਨਾਲ ਤੁਲਨਾ ਕਰਨ ਲਈ “ਕੀ ਅਸੀਂ Scala ਚੁਣੀਏ?” ਵੱਲੇ ਗੱਲਬਾਤ ਨੂੰ ਜ਼ਿਆਦਾ ਠੋਸ ਬਣਾਉਂਦਾ ਹੈ—ਭਾਸ਼ਾ ਫੀਚਰਾਂ ਦੀ ਬਜਾਏ ਵਰਕਫਲੋਜ਼, ਡਿਪਲੋਇਮੈਂਟ ਅਤੇ ਰੱਖ-ਰੱਖਾਅ ਦੇ ਆਧਾਰ 'ਤੇ।
Scala ਨੂੰ JVM ਦੇ ਆਮ ਦਰਦ-ਬਿੰਦੂ ਘਟਾਉਣ ਲਈ ਤਿਆਰ ਕੀਤਾ ਗਿਆ—ਬਹੁਤ ਸਾਰਾ ਬੋਇਲਰਪਲੇਟ, null ਨਾਲ ਜੁੜੀਆਂ ਬੱਗਾਂ ਅਤੇ ਜ਼ਿਆਦਾ-ਵਿਰਾਸਤ-ਨਿਰਭਰ ਡਿਜ਼ਾਈਨਾਂ—ਤਾਂ ਜੋ JVM ਦੀ ਪ੍ਰਦਰਸ਼ਨ, ਟੂਲਿੰਗ ਅਤੇ ਲਾਇਬ੍ਰੇਰੀ ਤੱਕ ਪਹੁੰਚ ਕਾਇਮ ਰਹੇ। ਮਕਸਦ ਇਹ ਸੀ ਕਿ ਡੋਮੇਨ ਲੌਜਿਕ ਨੂੰ ਤੇਜ਼ ਅਤੇ ਸਪੱਸ਼ਟ ਤਰੀਕੇ ਨਾਲ ਲਿਖਿਆ ਜਾਵੇ ਬਿਨਾਂ Java ਪਰੀਵਾਰ ਤੋਂ ਦੂਰ ਹੋਏ।
OO ਨੂੰ ਮਾਡਿਊਲ ਬਾਰਡਰ (API, ਇਨਕੈਪਸੁਲੇਸ਼ਨ, ਸੇਵਾ ਇੰਟਰਫੇਸ) ਤਿਆਰ ਕਰਨ ਲਈ ਵਰਤੋ, ਅਤੇ ਉਹਨਾਂ ਸੀਮਾਵਾਂ ਦੇ ਅੰਦਰ FP ਤਕਨੀਕਾਂ (ਅਪਰਿਵਰਤੀ ਡਾਟਾ, ਐਕਸਪ੍ਰੈਸ਼ਨ-ਉਤਪਾਦਕ ਕੋਡ, ਪਿਊਰ-ਜਿਹਾ ਫੰਕਸ਼ਨ) ਵਰਤ ਕੇ ਛਪਿਆ ਰਾਜ਼/ਸਟੇਟ ਘਟਾਓ ਅਤੇ ਟੈਸਟਿੰਗ ਤੇ ਬਦਲਾਅ ਆਸਾਨ ਬਣਾਓ।
val ਨੂੰ ਡਿਫ਼ਾਲਟ ਤੌਰ 'ਤੇ ਵਰਤੋ ਤਾਂ ਕਿ ਗਲਤੀ ਨਾਲ ਰੀਅਸਾਈਨਮੈਂਟ ਤੋਂ ਬਚਿਆ ਜਾ ਸਕੇ ਅਤੇ ਲੁਕੇ ਹੋਏ ਸਟੇਟ ਘਟੇ। var ਨੂੰ ਸੁਚੇਤ ਤਰੀਕੇ ਨਾਲ ਹੀ ਵਰਤੋ—ਛੋਟੀ, ਸਥਾਨਕ ਜਗ੍ਹਾਂ ਲਈ ਜਿਵੇਂ UI glue ਕੋਡ ਜਾਂ ਪਰਫਾਰਮੈਂਸ-ਸੰਵੇਦਨਸ਼ੀਲ ਲੂਪ—ਅਤੇ ਕੋਰ ਬਿਜਨਸ ਲੌਜਿਕ ਵਿੱਚ ਮਿਊਟੇਸ਼ਨ ਨ ਰੱਖੋ।
Traits ਉਹ “ਕਾਬਲियतਾਂ” ਹਨ ਜੋ ਤੁਸੀਂ ਕਈ ਵੱਖ-ਵੱਖ ਕਲਾਸਾਂ ਵਿੱਚ ਮਿਲਾ ਸਕਦੇ ਹੋ, ਇਸਤੋਂ ਡੂੰਘੀ ਅਤੇ ਨਾਜ਼ੁਕ ਵਿਰਾਸਤ ਨਹੀਂ ਬਣਦੀ।
sealed trait ਦੇ ਨਾਲ case class/case object ਵਰਤ ਕੇ ਇੱਕ ਬੰਦ ਸੈੱਟ ਦੀ ਰਾਜ ਸਪੱਸ਼ਟ ਕਰੋ, ਫਿਰ match ਨਾਲ ਹਰ ਕੇਸ ਨੂੰ ਦੇਖੋ ਅਤੇ ਹੈਂਡਲ ਕਰੋ.
ਇਸ ਤਰ੍ਹਾਂ ਗਲਤ ਸਥਿਤੀਆਂ ਨੂੰ ਦਰਸਾਉਣਾ ਮੁਸ਼ਕਲ ਹੋ ਜਾਂਦਾ ਹੈ ਅਤੇ ਕੰਪਾਇਲਰ ਤੁਹਾਨੂੰ ਚੇਤਾਵਨੀ ਦੇ ਸਕਦਾ ਹੈ ਜੇ ਕੋਈ ਨਵਾਂ ਕੇਸ ਅਣਹੈਂਡਲ ਰਿਹਾ ਹੋਵੇ।
ਟਾਈਪ ਇਨਫ਼ਰੇਂਸ ਨਾਲ ਤੁਸੀਂ ਹਰ ਜਗ੍ਹਾ ਟਾਈਪ ਲਿਖਣ ਦੀ ਲੋੜ ਘਟਾਉਂਦੇ ਹੋ ਪਰ ਫਿਰ ਵੀ ਕੋਡ ਨੂੰ ਕਾਫ਼ੀ ਸਖ਼ਤ ਚੈੱਕ ਮਿਲਦੇ ਹਨ।
ਸਮਾਨ ਅਮਲ ਇਹ ਹੈ ਕਿ ਸਰਹੱਦਾਂ ('public' ਮੈਥਡ, ਮੋਡੀਊਲ APIs, ਕਠਨ ਜੈਨੇਰਿਕਸ) 'ਤੇ ਸਪੱਸ਼ਟ ਟਾਈਪ ਲਿਖੋ ਤਾਂ ਕਿ ਪੜ੍ਹਨ ਵਾਲਿਆਂ ਲਈ ਇਰਾਦਾ ਸਪੱਸ਼ਟ ਰਹੇ ਅਤੇ ਕੰਪਾਇਲਰ-errors ਸਥਿਰ ਰਹਿਣ।
Variance ਇਸ ਗੱਲ ਬਾਰੇ ਹੈ ਕਿ ਜੈਨੇਰਿਕ ਟਾਈਪਸ ਵਿੱਚ ਉਪਟਾਈਪਿੰਗ ਕਿਵੇਂ ਕੰਮ ਕਰਦੀ ਹੈ:
+A): ਕੰਟੇਨਰ ਨੂੰ ਵਿਆਪਕ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ (ਉਦਾਹਰਣ: ਨੂੰ ਵਾਂਗ ਵਰਤਣਾ).ਇਹ ਉਹ ਤਰੀਕੇ ਹਨ ਜਿੰਨ੍ਹਾਂ ਨਾਲ ਤੁਸੀਂ ਕਿਸੇ ਟਾਈਪ ਨੂੰ ਬਿਨਾਂ ਉਸਨੂੰ ਬਦਲੇ ਵੱਖ-ਵੱਖ ਵਿਹਾਰ ਦੇ ਸਕਦੇ ਹੋ—ਯਾਨੀ type-class ਅੰਦਾਜ਼:
implicit\n- Scala 3: given / using\n
Scala 3 ਵਿੱਚ given/using ਨਾਲ ਇरਾਦਾ ਜ਼ਿਆਦਾ ਸਾਫ਼ ਹੁੰਦਾ ਹੈ—ਕਿਹੜਾ ਉਦਾਹਰਣ ਦਿੱਤਾ ਜਾ ਰਿਹਾ ਹੈ ਅਤੇ ਕਿਹੜਾ ਲੋੜੀਂਦਾ ਹੈ।ਸਾਡਾ ਸੁਝਾਅ: ਸਧਾਰਣ ਤੋਂ ਸ਼ੁਰੂ ਕਰੋ ਅਤੇ ਜਰੂਰਤ ਪੈਂਦੇ ਹੀ ਬਹੁਤ ਉੱਚੇ ਪੱਧਰ ਤੇ ਜਾਓ:
ਹਰ ਹਾਲਤ ਵਿੱਚ, ਅਪਰਿਵਰਤੀ ਡਾਟਾ ਭੇਜਣਾ ਰੇਸ-ਕੰਡਿਸ਼ਨ ਘਟਾਉਂਦਾ ਹੈ।
Java/Scala ਹੱਦਾਂ ਨੂੰ ਇੱਕ ਅਨੁਵਾਦ-ਤਹਿ ਵਜੋਂ ਦੇਖੋ:
null ਨੂੰ ੋਕੇ-ਫੋਕੇ Option ਵਿੱਚ ਤੁਰੰਤ ਬਦਲੋ (ਅਤੇ ਸਿਰਫ਼ ਏਜ 'ਤੇ ਵਾਪਸ null ਵਿੱਚ ਬਦਲੋ).ਇਸ ਨਾਲ interop ਅਨੁਮਾਨਯੋਗ ਰਹਿੰਦੀ ਹੈ ਅਤੇ Java ਦੇ ਡਿਫੋਲਟ (nulls, mutation) ਵਿੱਚੋਂ ਲੀਕ ਘਟਦਾ ਹੈ।
List[Cat]List[Animal]-A): ਇੱਕ ਖਪਤਕਾਰ/ਹੈਂਡਲਰ ਨੂੰ ਵਿਆਪਕ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ (ਉਦਾਹਰਣ: Handler[Animal] ਨੂੰ Handler[Cat] ਦੀ ਥਾਂ ਰੱਖਣਾ).ਤੁਸੀਂ ਇਹ ਸਭ ਤੋਂ ਜ਼ਿਆਦਾ ਲਾਇਬ੍ਰੇਰੀ ਜਾਂ API ਡਿਜ਼ਾਈਨ ਸਮੇਂ ਮਹਿਸੂਸ ਕਰੋਗੇ।